| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | /** | 
					
						
							|  |  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02: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-08-10 12:51:30 +01:00
										 |  |  |  | // Make the `$localize()` global function available to the compiled templates, and the direct calls
 | 
					
						
							|  |  |  |  | // below. This would normally be done inside the application `polyfills.ts` file.
 | 
					
						
							|  |  |  |  | import '@angular/localize/init'; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 17:52:49 -07:00
										 |  |  |  | import {CommonModule, DOCUMENT, registerLocaleData} from '@angular/common'; | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | import localeEs from '@angular/common/locales/es'; | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | import localeRo from '@angular/common/locales/ro'; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  | import {computeMsgId} from '@angular/compiler'; | 
					
						
							| 
									
										
										
										
											2020-10-24 13:16:11 +02:00
										 |  |  |  | import {Attribute, Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, Type, ViewChild, ViewContainerRef, ɵsetDocument} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  | import {DebugNode, HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view'; | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  | import {getComponentLView} from '@angular/core/src/render3/util/discovery_utils'; | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | import {TestBed} from '@angular/core/testing'; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  | import {clearTranslations, loadTranslations} from '@angular/localize'; | 
					
						
							| 
									
										
										
										
											2020-05-05 17:52:49 -07:00
										 |  |  |  | import {By, ɵDomRendererFactory2 as DomRendererFactory2} from '@angular/platform-browser'; | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | import {expect} from '@angular/platform-browser/testing/src/matchers'; | 
					
						
							|  |  |  |  | import {onlyInIvy} from '@angular/private/testing'; | 
					
						
							| 
									
										
										
										
											2019-12-02 13:58:32 -08:00
										 |  |  |  | import {BehaviorSubject} from 'rxjs'; | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { | 
					
						
							|  |  |  |  |   beforeEach(() => { | 
					
						
							| 
									
										
										
										
											2019-10-26 12:44:03 +02:00
										 |  |  |  |     TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |       declarations: [AppComp, DirectiveWithTplRef, UppercasePipe], | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       // In some of the tests we use made-up tag names for better readability, however
 | 
					
						
							|  |  |  |  |       // they'll cause validation errors. Add the `NO_ERRORS_SCHEMA` so that we don't have
 | 
					
						
							|  |  |  |  |       // to declare dummy components for each one of them.
 | 
					
						
							| 
									
										
										
										
											2019-10-26 12:44:03 +02:00
										 |  |  |  |       schemas: [NO_ERRORS_SCHEMA], | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |   afterEach(() => { | 
					
						
							|  |  |  |  |     clearTranslations(); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should translate text', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('text')]: 'texte'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppComp, `<div i18n>text</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div>texte</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support interpolations', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('Hello {$INTERPOLATION}!')]: 'Bonjour {$INTERPOLATION}!'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppComp, `<div i18n>Hello {{name}}!</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div>Bonjour Angular!</div>`); | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.name = `John`; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div>Bonjour John!</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support named interpolations', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId(' Hello {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ')]: | 
					
						
							|  |  |  |  |           ' Bonjour {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ' | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         Hello {{ name // i18n(ph="user_name") }}!
 | 
					
						
							|  |  |  |  |         Emails: {{ count // i18n(ph="amount of emails received") }}
 | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div> Bonjour Angular! Emails: 0 </div>`); | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.name = `John`; | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.count = 5; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div> Bonjour John! Emails: 5 </div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support interpolations with custom interpolation config', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |     const interpolation = ['{%', '%}'] as [string, string]; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     TestBed.overrideComponent(AppComp, {set: {interpolation}}); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `<div i18n>Hello {% name %}</div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toBe('<div>Bonjour Angular</div>'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support &ngsp; in translatable sections', () => { | 
					
						
							|  |  |  |  |     // note: the `` unicode symbol represents the `&ngsp;` in translations
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('text ||')]: 'texte ||'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppCompWithWhitespaces, `<div i18n>text |&ngsp;|</div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div>texte | |</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support interpolations with complex expressions', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId(' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} ')]: | 
					
						
							|  |  |  |  |           ' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr) ' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-08-24 14:02:30 -07:00
										 |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         {{ name | uppercase }} - | 
					
						
							|  |  |  |  |         {{ obj?.a?.b }} - | 
					
						
							|  |  |  |  |         {{ obj?.getA()?.b }} | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |     // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty
 | 
					
						
							|  |  |  |  |     // strings
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div> ANGULAR -  -  (fr) </div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.obj = { | 
					
						
							|  |  |  |  |       a: {b: 'value 1'}, | 
					
						
							|  |  |  |  |       getA: () => ({b: 'value 2'}), | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div> ANGULAR - value 1 - value 2 (fr) </div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support elements', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           'Hello {$START_TAG_SPAN}world{$CLOSE_TAG_SPAN} and {$START_TAG_DIV}universe{$CLOSE_TAG_DIV}!', | 
					
						
							|  |  |  |  |           '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |           'Bonjour {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN} et {$START_TAG_DIV}univers{$CLOSE_TAG_DIV}!' | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |         AppComp, `<div i18n>Hello <span>world</span> and <div>universe</div>!</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div>Bonjour <span>monde</span> et <div>univers</div>!</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support removing elements', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           'Hello {$START_BOLD_TEXT}my{$CLOSE_BOLD_TEXT}{$START_TAG_SPAN}world{$CLOSE_TAG_SPAN}', | 
					
						
							|  |  |  |  |           '')]: 'Bonjour {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN}' | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = | 
					
						
							|  |  |  |  |         initWithTemplate(AppComp, `<div i18n>Hello <b>my</b><span>world</span></div><div>!</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div>Bonjour <span>monde</span></div><div>!</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support moving elements', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           'Hello {$START_TAG_SPAN}world{$CLOSE_TAG_SPAN} and {$START_TAG_DIV}universe{$CLOSE_TAG_DIV}!', | 
					
						
							|  |  |  |  |           '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |           'Bonjour {$START_TAG_DIV}univers{$CLOSE_TAG_DIV} et {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN}!' | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |         AppComp, `<div i18n>Hello <span>world</span> and <div>universe</div>!</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div>Bonjour <div>univers</div> et <span>monde</span>!</div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support template directives', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           'Content: {$START_TAG_DIV}before{$START_TAG_SPAN}middle{$CLOSE_TAG_SPAN}after{$CLOSE_TAG_DIV}!', | 
					
						
							|  |  |  |  |           '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |           'Contenu: {$START_TAG_DIV}avant{$START_TAG_SPAN}milieu{$CLOSE_TAG_SPAN}après{$CLOSE_TAG_DIV}!' | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |         AppComp, | 
					
						
							|  |  |  |  |         `<div i18n>Content: <div *ngIf="visible">before<span>middle</span>after</div>!</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div>Contenu: <div>avant<span>milieu</span>après</div><!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |   "ng-reflect-ng-if": "true" | 
					
						
							|  |  |  |  | }-->!</div>`);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     fixture.componentRef.instance.visible = false; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div>Contenu: <!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |   "ng-reflect-ng-if": "false" | 
					
						
							|  |  |  |  | }-->!</div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support multiple i18n blocks', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId('trad {$INTERPOLATION}')]: 'traduction {$INTERPOLATION}', | 
					
						
							|  |  |  |  |       [computeMsgId('start {$INTERPOLATION} middle {$INTERPOLATION_1} end')]: | 
					
						
							|  |  |  |  |           'start {$INTERPOLATION_1} middle {$INTERPOLATION} end', | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{$START_TAG_C}trad{$CLOSE_TAG_C}{$START_TAG_D}{$CLOSE_TAG_D}{$START_TAG_E}{$CLOSE_TAG_E}', | 
					
						
							|  |  |  |  |           '')]: '{$START_TAG_E}{$CLOSE_TAG_E}{$START_TAG_C}traduction{$CLOSE_TAG_C}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |       <div> | 
					
						
							|  |  |  |  |         <a i18n>trad {{name}}</a> | 
					
						
							|  |  |  |  |         hello | 
					
						
							|  |  |  |  |         <b i18n i18n-title title="start {{count}} middle {{name}} end"> | 
					
						
							|  |  |  |  |           <c>trad</c> | 
					
						
							|  |  |  |  |           <d></d> | 
					
						
							|  |  |  |  |           <e></e> | 
					
						
							|  |  |  |  |         </b> | 
					
						
							|  |  |  |  |       </div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             `<div><a>traduction Angular</a> hello <b title="start Angular middle 0 end"><e></e><c>traduction</c></b></div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support multiple sibling i18n blocks', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId('Section 1')]: 'Section un', | 
					
						
							|  |  |  |  |       [computeMsgId('Section 2')]: 'Section deux', | 
					
						
							|  |  |  |  |       [computeMsgId('Section 3')]: 'Section trois', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |       <div> | 
					
						
							|  |  |  |  |         <div i18n>Section 1</div> | 
					
						
							|  |  |  |  |         <div i18n>Section 2</div> | 
					
						
							|  |  |  |  |         <div i18n>Section 3</div> | 
					
						
							|  |  |  |  |       </div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual(`<div><div>Section un</div><div>Section deux</div><div>Section trois</div></div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support multiple sibling i18n blocks inside of a template directive', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId('Section 1')]: 'Section un', | 
					
						
							|  |  |  |  |       [computeMsgId('Section 2')]: 'Section deux', | 
					
						
							|  |  |  |  |       [computeMsgId('Section 3')]: 'Section trois', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |       <ul *ngFor="let item of [1,2,3]"> | 
					
						
							|  |  |  |  |         <li i18n>Section 1</li> | 
					
						
							|  |  |  |  |         <li i18n>Section 2</li> | 
					
						
							|  |  |  |  |         <li i18n>Section 3</li> | 
					
						
							|  |  |  |  |       </ul>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             `<ul><li>Section un</li><li>Section deux</li><li>Section trois</li></ul><ul><li>Section un</li><li>Section deux</li><li>Section trois</li></ul><ul><li>Section un</li><li>Section deux</li><li>Section trois</li></ul><!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |   "ng-reflect-ng-for-of": "1,2,3" | 
					
						
							|  |  |  |  | }-->`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should properly escape quotes in content', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId('\'Single quotes\' and "Double quotes"')]: | 
					
						
							|  |  |  |  |           '\'Guillemets simples\' et "Guillemets doubles"' | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = | 
					
						
							|  |  |  |  |         initWithTemplate(AppComp, `<div i18n>'Single quotes' and "Double quotes"</div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual('<div>\'Guillemets simples\' et "Guillemets doubles"</div>'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should correctly bind to context in nested template', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('Item {$INTERPOLATION}')]: 'Article {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |           <div *ngFor='let id of items'> | 
					
						
							|  |  |  |  |             <div i18n>Item {{ id }}</div> | 
					
						
							|  |  |  |  |           </div> | 
					
						
							|  |  |  |  |         `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |     for (let i = 0; i < element.children.length; i++) { | 
					
						
							|  |  |  |  |       const child = element.children[i]; | 
					
						
							|  |  |  |  |       expect(child).toHaveText(`Article ${i + 1}`); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should ignore i18n attributes on self-closing tags', () => { | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, '<img src="logo.png" i18n>'); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.innerHTML).toBe(`<img src="logo.png">`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should handle i18n attribute with directives', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = initWithTemplate(AppComp, `<div *ngIf="visible" i18n>Hello {{ name }}</div>`); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.firstChild).toHaveText('Bonjour Angular'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should work correctly with event listeners', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component( | 
					
						
							|  |  |  |  |         {selector: 'app-comp', template: `<div i18n (click)="onClick()">Hello {{ name }}</div>`}) | 
					
						
							|  |  |  |  |     class ListenerComp { | 
					
						
							|  |  |  |  |       name = `Angular`; | 
					
						
							|  |  |  |  |       clicks = 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       onClick() { | 
					
						
							|  |  |  |  |         this.clicks++; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [ListenerComp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(ListenerComp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const element = fixture.nativeElement.firstChild; | 
					
						
							|  |  |  |  |     const instance = fixture.componentInstance; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(element).toHaveText('Bonjour Angular'); | 
					
						
							|  |  |  |  |     expect(instance.clicks).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     element.click(); | 
					
						
							|  |  |  |  |     expect(instance.clicks).toBe(1); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 16:54:27 -07:00
										 |  |  |  |   it('should support local refs inside i18n block', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_CONTAINER} One {$CLOSE_TAG_NG_CONTAINER}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV} Two {$CLOSE_TAG_DIV}' + | 
					
						
							| 
									
										
										
										
											2020-02-28 09:07:37 -08:00
										 |  |  |  |           '{$START_TAG_SPAN} Three {$CLOSE_TAG_SPAN}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_TEMPLATE} Four {$CLOSE_TAG_NG_TEMPLATE}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_CONTAINER_1}{$CLOSE_TAG_NG_CONTAINER}')]: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 16:54:27 -07:00
										 |  |  |  |           '{$START_TAG_NG_CONTAINER} Une {$CLOSE_TAG_NG_CONTAINER}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV} Deux {$CLOSE_TAG_DIV}' + | 
					
						
							| 
									
										
										
										
											2020-02-28 09:07:37 -08:00
										 |  |  |  |           '{$START_TAG_SPAN} Trois {$CLOSE_TAG_SPAN}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_TEMPLATE} Quatre {$CLOSE_TAG_NG_TEMPLATE}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_CONTAINER_1}{$CLOSE_TAG_NG_CONTAINER}' | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 16:54:27 -07:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         <ng-container #localRefA> One </ng-container> | 
					
						
							|  |  |  |  |         <div #localRefB> Two </div> | 
					
						
							|  |  |  |  |         <span #localRefC> Three </span> | 
					
						
							| 
									
										
										
										
											2020-02-28 09:07:37 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         <ng-template #localRefD> Four </ng-template> | 
					
						
							|  |  |  |  |         <ng-container *ngTemplateOutlet="localRefD"></ng-container> | 
					
						
							| 
									
										
										
										
											2019-10-25 16:54:27 -07:00
										 |  |  |  |       </div> | 
					
						
							|  |  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2020-02-28 09:07:37 -08:00
										 |  |  |  |     expect(fixture.nativeElement.textContent).toBe(' Une  Deux  Trois  Quatre '); | 
					
						
							| 
									
										
										
										
											2019-10-25 16:54:27 -07:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should handle local refs correctly in case an element is removed in translation', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{$START_TAG_NG_CONTAINER} One {$CLOSE_TAG_NG_CONTAINER}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV} Two {$CLOSE_TAG_DIV}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_SPAN} Three {$CLOSE_TAG_SPAN}')]: '{$START_TAG_DIV} Deux {$CLOSE_TAG_DIV}' | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         <ng-container #localRefA> One </ng-container> | 
					
						
							|  |  |  |  |         <div #localRefB> Two </div> | 
					
						
							|  |  |  |  |         <span #localRefC> Three </span> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     `);
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toBe(' Deux '); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   describe('ng-container and ng-template support', () => { | 
					
						
							|  |  |  |  |     it('should support ng-container', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('text')]: 'texte'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `<ng-container i18n>text</ng-container>`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`texte<!--ng-container-->`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should handle single translation message within ng-template', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<ng-template i18n tplRef>Hello {{ name }}</ng-template>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('Bonjour Angular'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Note: applying structural directives to <ng-template> is typically user error, but it
 | 
					
						
							|  |  |  |  |     // is technically allowed, so we need to support it.
 | 
					
						
							|  |  |  |  |     it('should handle structural directives on ng-template', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |           AppComp, `<ng-template *ngIf="name" i18n tplRef>Hello {{ name }}</ng-template>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('Bonjour Angular'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should be able to act as child elements inside i18n block (plain text content)', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_TEMPLATE} Hello {$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER} Bye {$CLOSE_TAG_NG_CONTAINER}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{$START_TAG_NG_TEMPLATE} Bonjour {$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER} Au revoir {$CLOSE_TAG_NG_CONTAINER}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           <ng-template tplRef> | 
					
						
							|  |  |  |  |             Hello | 
					
						
							|  |  |  |  |           </ng-template> | 
					
						
							|  |  |  |  |           <ng-container> | 
					
						
							|  |  |  |  |             Bye | 
					
						
							|  |  |  |  |           </ng-container> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement.firstChild; | 
					
						
							|  |  |  |  |       expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Bonjour Au revoir'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('should be able to act as child elements inside i18n block (text + tags)', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_TEMPLATE}{$START_TAG_SPAN}Hello{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}{$START_TAG_SPAN}Hello{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_CONTAINER}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{$START_TAG_NG_TEMPLATE}{$START_TAG_SPAN}Bonjour{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}{$START_TAG_SPAN}Bonjour{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_CONTAINER}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           <ng-template tplRef> | 
					
						
							|  |  |  |  |             <span>Hello</span> | 
					
						
							|  |  |  |  |           </ng-template> | 
					
						
							|  |  |  |  |           <ng-container> | 
					
						
							|  |  |  |  |             <span>Hello</span> | 
					
						
							|  |  |  |  |           </ng-container> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       const spans = element.getElementsByTagName('span'); | 
					
						
							|  |  |  |  |       for (let i = 0; i < spans.length; i++) { | 
					
						
							|  |  |  |  |         expect(spans[i]).toHaveText('Bonjour'); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should be able to act as child elements inside i18n block (text + pipes)', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_TEMPLATE}Hello {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}Bye {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{$START_TAG_NG_TEMPLATE}Hej {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}Vi ses {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-07-07 16:35:58 +02:00
										 |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           <ng-template tplRef>Hello {{name | uppercase}}</ng-template> | 
					
						
							|  |  |  |  |           <ng-container>Bye {{name | uppercase}}</ng-container> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement.firstChild; | 
					
						
							|  |  |  |  |       expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Hej ANGULARVi ses ANGULAR'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-08-08 22:05:43 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('should be able to handle deep nested levels with templates', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN} Hello - 1 {$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1} Hello - 2 {$START_TAG_SPAN_1} Hello - 3 {$START_TAG_SPAN_1} Hello - 4 {$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN} Hello - 5 {$CLOSE_TAG_SPAN}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{$START_TAG_SPAN} Bonjour - 1 {$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1} Bonjour - 2 {$START_TAG_SPAN_1} Bonjour - 3 {$START_TAG_SPAN_1} Bonjour - 4 {$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN} Bonjour - 5 {$CLOSE_TAG_SPAN}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           <span> | 
					
						
							|  |  |  |  |             Hello - 1 | 
					
						
							|  |  |  |  |           </span> | 
					
						
							|  |  |  |  |           <span *ngIf="visible"> | 
					
						
							|  |  |  |  |             Hello - 2 | 
					
						
							|  |  |  |  |             <span *ngIf="visible"> | 
					
						
							|  |  |  |  |               Hello - 3 | 
					
						
							|  |  |  |  |               <span *ngIf="visible"> | 
					
						
							|  |  |  |  |                 Hello - 4 | 
					
						
							|  |  |  |  |               </span> | 
					
						
							|  |  |  |  |             </span> | 
					
						
							|  |  |  |  |           </span> | 
					
						
							|  |  |  |  |           <span> | 
					
						
							|  |  |  |  |             Hello - 5 | 
					
						
							|  |  |  |  |           </span> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       const spans = element.getElementsByTagName('span'); | 
					
						
							|  |  |  |  |       for (let i = 0; i < spans.length; i++) { | 
					
						
							|  |  |  |  |         expect(spans[i].innerHTML).toContain(`Bonjour - ${i + 1}`); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should handle self-closing tags as content', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}My logo{$TAG_IMG}{$CLOSE_TAG_SPAN}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}Mon logo{$TAG_IMG}{$CLOSE_TAG_SPAN}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const content = `My logo<img src="logo.png" title="Logo">`; | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         <ng-container i18n> | 
					
						
							|  |  |  |  |           <span>${content}</span> | 
					
						
							|  |  |  |  |         </ng-container> | 
					
						
							|  |  |  |  |         <ng-template i18n tplRef> | 
					
						
							|  |  |  |  |           <span>${content}</span> | 
					
						
							|  |  |  |  |         </ng-template> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       const spans = element.getElementsByTagName('span'); | 
					
						
							|  |  |  |  |       for (let i = 0; i < spans.length; i++) { | 
					
						
							|  |  |  |  |         const child = spans[i]; | 
					
						
							|  |  |  |  |         expect(child).toHaveText('Mon logo'); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should correctly find context for an element inside i18n section in <ng-template>', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_LINK}Not logged in{$CLOSE_LINK}')]: | 
					
						
							|  |  |  |  |             '{$START_LINK}Not logged in{$CLOSE_LINK}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Directive({selector: '[myDir]'}) | 
					
						
							|  |  |  |  |       class Dir { | 
					
						
							|  |  |  |  |         condition = true; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-cmp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-07 18:20:35 +02:00
										 |  |  |  |               <div *ngIf="isLogged; else notLoggedIn"> | 
					
						
							|  |  |  |  |                 <span>Logged in</span> | 
					
						
							|  |  |  |  |               </div> | 
					
						
							|  |  |  |  |               <ng-template #notLoggedIn i18n> | 
					
						
							|  |  |  |  |                 <a myDir>Not logged in</a> | 
					
						
							|  |  |  |  |               </ng-template> | 
					
						
							|  |  |  |  |             `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |         isLogged = false; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         declarations: [Cmp, Dir], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Cmp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const a = fixture.debugElement.query(By.css('a')); | 
					
						
							|  |  |  |  |       const dir = a.injector.get(Dir); | 
					
						
							|  |  |  |  |       expect(dir.condition).toEqual(true); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 17:52:49 -07:00
										 |  |  |  |   describe('should work correctly with namespaces', () => { | 
					
						
							|  |  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |  |       function _document(): any { | 
					
						
							|  |  |  |  |         // Tell Ivy about the global document
 | 
					
						
							|  |  |  |  |         ɵsetDocument(document); | 
					
						
							|  |  |  |  |         return document; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         providers: [ | 
					
						
							|  |  |  |  |           {provide: DOCUMENT, useFactory: _document, deps: []}, | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |           // TODO(FW-811): switch back to default server renderer (i.e. remove the line
 | 
					
						
							|  |  |  |  |           // below) once it starts to support Ivy namespace format (URIs) correctly. For
 | 
					
						
							|  |  |  |  |           // now, use `DomRenderer` that supports Ivy namespace format.
 | 
					
						
							| 
									
										
										
										
											2020-05-05 17:52:49 -07:00
										 |  |  |  |           {provide: RendererFactory2, useClass: DomRendererFactory2} | 
					
						
							|  |  |  |  |         ], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should handle namespaces inside i18n blocks', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG__XHTML_DIV} Hello ' + | 
					
						
							|  |  |  |  |             '{$START_TAG__XHTML_SPAN}world{$CLOSE_TAG__XHTML_SPAN}{$CLOSE_TAG__XHTML_DIV}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG__XHTML_DIV} Bonjour ' + | 
					
						
							|  |  |  |  |             '{$START_TAG__XHTML_SPAN}monde{$CLOSE_TAG__XHTML_SPAN}{$CLOSE_TAG__XHTML_DIV}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <svg xmlns="http://www.w3.org/2000/svg"> | 
					
						
							|  |  |  |  |           <foreignObject i18n> | 
					
						
							|  |  |  |  |             <xhtml:div xmlns="http://www.w3.org/1999/xhtml"> | 
					
						
							|  |  |  |  |               Hello <span>world</span> | 
					
						
							|  |  |  |  |             </xhtml:div> | 
					
						
							|  |  |  |  |           </foreignObject> | 
					
						
							|  |  |  |  |         </svg> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element.textContent.trim()).toBe('Bonjour monde'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('svg').namespaceURI).toBe('http://www.w3.org/2000/svg'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('div').namespaceURI).toBe('http://www.w3.org/1999/xhtml'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('span').namespaceURI).toBe('http://www.w3.org/1999/xhtml'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should handle namespaces on i18n block containers', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId(' Hello {$START_TAG__XHTML_SPAN}world{$CLOSE_TAG__XHTML_SPAN}')]: | 
					
						
							|  |  |  |  |             ' Bonjour {$START_TAG__XHTML_SPAN}monde{$CLOSE_TAG__XHTML_SPAN}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <svg xmlns="http://www.w3.org/2000/svg"> | 
					
						
							|  |  |  |  |           <foreignObject> | 
					
						
							|  |  |  |  |             <xhtml:div xmlns="http://www.w3.org/1999/xhtml" i18n> | 
					
						
							|  |  |  |  |               Hello <span>world</span> | 
					
						
							|  |  |  |  |             </xhtml:div> | 
					
						
							|  |  |  |  |           </foreignObject> | 
					
						
							|  |  |  |  |         </svg> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element.textContent.trim()).toBe('Bonjour monde'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('svg').namespaceURI).toBe('http://www.w3.org/2000/svg'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('div').namespaceURI).toBe('http://www.w3.org/1999/xhtml'); | 
					
						
							|  |  |  |  |       expect(element.querySelector('span').namespaceURI).toBe('http://www.w3.org/1999/xhtml'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |   describe('dynamic TNodes', () => { | 
					
						
							|  |  |  |  |     // When translation occurs the i18n system needs to create dynamic TNodes for the text
 | 
					
						
							|  |  |  |  |     // nodes so that they can be correctly processed by the `addRemoveViewFromContainer`.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     function toTypeContent(n: DebugNode): string { | 
					
						
							|  |  |  |  |       return `${n.type}(${n.html})`; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should not create dynamic TNode when no i18n', () => { | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `Hello <b>World</b>!`); | 
					
						
							|  |  |  |  |       const lView = getComponentLView(fixture.componentInstance); | 
					
						
							|  |  |  |  |       const hello_ = (fixture.nativeElement as Element).firstChild!; | 
					
						
							|  |  |  |  |       const b = hello_.nextSibling!; | 
					
						
							|  |  |  |  |       const world = b.firstChild!; | 
					
						
							|  |  |  |  |       const exclamation = b.nextSibling!; | 
					
						
							|  |  |  |  |       const lViewDebug = lView.debug!; | 
					
						
							|  |  |  |  |       expect(lViewDebug.nodes.map(toTypeContent)).toEqual([ | 
					
						
							| 
									
										
										
										
											2020-10-12 21:38:06 -07:00
										 |  |  |  |         'Text(Hello )', 'Element(<b>)', 'Text(!)' | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |       ]); | 
					
						
							|  |  |  |  |       expect(lViewDebug.decls).toEqual({ | 
					
						
							|  |  |  |  |         start: HEADER_OFFSET, | 
					
						
							|  |  |  |  |         end: HEADER_OFFSET + 4, | 
					
						
							|  |  |  |  |         length: 4, | 
					
						
							|  |  |  |  |         content: [ | 
					
						
							|  |  |  |  |           jasmine.objectContaining({index: HEADER_OFFSET + 0, l: hello_}), | 
					
						
							|  |  |  |  |           jasmine.objectContaining({index: HEADER_OFFSET + 1, l: b}), | 
					
						
							|  |  |  |  |           jasmine.objectContaining({index: HEADER_OFFSET + 2, l: world}), | 
					
						
							|  |  |  |  |           jasmine.objectContaining({index: HEADER_OFFSET + 3, l: exclamation}), | 
					
						
							|  |  |  |  |         ] | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       expect(lViewDebug.expando) | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               {start: lViewDebug.vars.end, end: lViewDebug.expando.start, length: 0, content: []}); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     describe('ICU', () => { | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       // In the case of ICUs we can't create TNodes for each ICU part, as different ICU
 | 
					
						
							|  |  |  |  |       // instances may have different selections active and hence have different shape. In
 | 
					
						
							|  |  |  |  |       // such a case a single `TIcuContainerNode` should be generated only.
 | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |       it('should create a single dynamic TNode for ICU', () => { | 
					
						
							|  |  |  |  |         const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2020-10-24 13:16:11 +02:00
										 |  |  |  |           {count, plural, | 
					
						
							|  |  |  |  |             =0 {just now} | 
					
						
							|  |  |  |  |             =1 {one minute ago} | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |             other {{{count}} minutes ago} | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         `.trim());
 | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |         const lView = getComponentLView(fixture.componentInstance); | 
					
						
							|  |  |  |  |         const lViewDebug = lView.debug!; | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |         expect((fixture.nativeElement as Element).textContent).toEqual('just now'); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |         expect(lViewDebug.nodes.map(toTypeContent)).toEqual(['IcuContainer(<!--ICU 20:0-->)']); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |         // We want to ensure that the ICU container does not have any content!
 | 
					
						
							|  |  |  |  |         // This is because the content is instance dependent and therefore can't be shared
 | 
					
						
							|  |  |  |  |         // across `TNode`s.
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         expect(lViewDebug.nodes[0].children.map(toTypeContent)).toEqual([]); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |         expect(fixture.nativeElement.innerHTML).toEqual('just now<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       it('should support multiple ICUs', () => { | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |         const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2020-10-24 13:16:11 +02:00
										 |  |  |  |           {count, plural, | 
					
						
							|  |  |  |  |             =0 {just now} | 
					
						
							|  |  |  |  |             =1 {one minute ago} | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |             other {{{count}} minutes ago} | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2020-10-24 13:16:11 +02:00
										 |  |  |  |           {name, select, | 
					
						
							|  |  |  |  |             Angular {Mr. Angular} | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |             other {Sir} | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         `);
 | 
					
						
							|  |  |  |  |         const lView = getComponentLView(fixture.componentInstance); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         expect(lView.debug!.nodes.map(toTypeContent)).toEqual([ | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           'IcuContainer(<!--ICU 20:0-->)', | 
					
						
							|  |  |  |  |           'IcuContainer(<!--ICU 21:0-->)', | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |         // We want to ensure that the ICU container does not have any content!
 | 
					
						
							|  |  |  |  |         // This is because the content is instance dependent and therefore can't be shared
 | 
					
						
							|  |  |  |  |         // across `TNode`s.
 | 
					
						
							|  |  |  |  |         expect(lView.debug!.nodes[0].children.map(toTypeContent)).toEqual([]); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |         expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |             .toEqual('just now<!--ICU 20:0-->Mr. Angular<!--ICU 21:0-->'); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   describe('should support ICU expressions', () => { | 
					
						
							|  |  |  |  |     it('with no root node', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `{count, select, 10 {ten} 20 {twenty} other {other}}`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('autre'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 14:44:13 -08:00
										 |  |  |  |     it('with no root node and text surrounding ICU', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {Ten} 20 {Twenty} other {Other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {Dix} 20 {Vingt} other {Autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         ICU start --> | 
					
						
							|  |  |  |  |         {count, select, 10 {Ten} 20 {Twenty} other {Other}} | 
					
						
							|  |  |  |  |         <-- ICU end | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element.textContent).toContain('ICU start --> Autre <-- ICU end'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 17:50:21 -07:00
										 |  |  |  |     it('when `select` or `plural` keywords have spaces after them', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT , select , 10 {ten} 20 {twenty} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT , select , 10 {dix} 20 {vingt} other {autre}}', | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_PLURAL , plural , =0 {zero} =1 {one} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL , plural , =0 {zéro} =1 {une} other {autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           {count, select , 10 {ten} 20 {twenty} other {other}} - | 
					
						
							|  |  |  |  |           {count, plural , =0 {zero} =1 {one} other {other}} | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element.textContent).toContain('autre - zéro'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 14:44:13 -08:00
										 |  |  |  |     it('with no root node and text and DOM nodes surrounding ICU', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {Ten} 20 {Twenty} other {Other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {Dix} 20 {Vingt} other {Autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <span>ICU start --> </span> | 
					
						
							|  |  |  |  |         {count, select, 10 {Ten} 20 {Twenty} other {Other}} | 
					
						
							|  |  |  |  |         <-- ICU end | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element.textContent).toContain('ICU start --> Autre <-- ICU end'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('with no i18n tag', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |           AppComp, `<div>{count, select, 10 {ten} 20 {twenty} other {other}}</div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('autre'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('multiple', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, other {({INTERPOLATION})}}', | 
					
						
							|  |  |  |  |         [computeMsgId('{$ICU} - {$ICU_1}')]: '{$ICU} - {$ICU_1}', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n>{count, plural,
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         =0 {no <b>emails</b>!} | 
					
						
							|  |  |  |  |         =1 {one <i>email</i>} | 
					
						
							|  |  |  |  |         other {{{count}} <span title="{{name}}">emails</span>} | 
					
						
							|  |  |  |  |       } - {name, select, | 
					
						
							|  |  |  |  |         other {({{name}})} | 
					
						
							|  |  |  |  |       }</div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toEqual(`<div>aucun <b>email</b>!<!--ICU 21:0--> - (Angular)<!--ICU 21:3--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<div>4 <span title="Angular">emails</span><!--ICU 21:0--> - (Angular)<!--ICU 21:3--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 0; | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'John'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toEqual(`<div>aucun <b>email</b>!<!--ICU 21:0--> - (John)<!--ICU 21:3--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with custom interpolation config', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION}}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {dix} other {{INTERPOLATION}}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       const interpolation = ['{%', '%}'] as [string, string]; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       TestBed.overrideComponent(AppComp, {set: {interpolation}}); | 
					
						
							|  |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<div i18n>{count, select, 10 {ten} other {{% name %}}}</div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement).toHaveText(`Angular`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('inside HTML elements', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, other {({INTERPOLATION})}}', | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - ' + | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n><span>{count, plural,
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         =0 {no <b>emails</b>!} | 
					
						
							|  |  |  |  |         =1 {one <i>email</i>} | 
					
						
							|  |  |  |  |         other {{{count}} <span title="{{name}}">emails</span>} | 
					
						
							|  |  |  |  |       }</span> - <span>{name, select, | 
					
						
							|  |  |  |  |         other {({{name}})} | 
					
						
							|  |  |  |  |       }</span></div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `<div>` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>aucun <b>email</b>!<!--ICU 21:0--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               ` - ` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>(Angular)<!--ICU 21:3--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `</div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `<div>` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>4 <span title="Angular">emails</span><!--ICU 21:0--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               ` - ` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>(Angular)<!--ICU 21:3--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `</div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 0; | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'John'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `<div>` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>aucun <b>email</b>!<!--ICU 21:0--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               ` - ` + | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<span>(John)<!--ICU 21:3--></span>` + | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |               `</div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('inside template directives', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}{$ICU}{$CLOSE_TAG_SPAN}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}{$ICU}{$CLOSE_TAG_SPAN}', | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, other {({INTERPOLATION})}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n><span *ngIf="visible">{name, select,
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         other {({{name}})} | 
					
						
							|  |  |  |  |       }</span></div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toEqual(`<div><span>(Angular)<!--ICU 20:0--></span><!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |   "ng-reflect-ng-if": "true" | 
					
						
							|  |  |  |  | }--></div>`);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       fixture.componentRef.instance.visible = false; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div><!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |   "ng-reflect-ng-if": "false" | 
					
						
							|  |  |  |  | }--></div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-08-08 22:05:43 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('inside ng-container', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, other {({INTERPOLATION})}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `<ng-container i18n>{name, select,
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         other {({{name}})} | 
					
						
							|  |  |  |  |       }</ng-container>`); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`(Angular)<!--ICU 21:0--><!--ng-container-->`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('inside <ng-template>', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate( | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |           AppComp, | 
					
						
							|  |  |  |  |           `
 | 
					
						
							| 
									
										
										
										
											2019-08-02 12:42:04 +01:00
										 |  |  |  |         <ng-template i18n tplRef>` +
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |               `{count, select, 10 {ten} 20 {twenty} other {other}}` + | 
					
						
							|  |  |  |  |               `</ng-template>
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |       `);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('autre'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('nested', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chiens} other {animaux}}!}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n>{count, plural,
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |         =0 {zero} | 
					
						
							|  |  |  |  |         other {{{count}} {name, select, | 
					
						
							|  |  |  |  |                        cat {cats} | 
					
						
							|  |  |  |  |                        dog {dogs} | 
					
						
							|  |  |  |  |                        other {animals} | 
					
						
							|  |  |  |  |                      }!} | 
					
						
							|  |  |  |  |       }</div>`);
 | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 21:1--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toEqual(`<div>4 animaux<!--nested ICU 0-->!<!--ICU 21:1--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('nested with interpolations in "other" blocks', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!} other {other - {INTERPOLATION}}}', | 
					
						
							|  |  |  |  |             '')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chiens} other {animaux}}!} other {autre - {INTERPOLATION}}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n>{count, plural,
 | 
					
						
							| 
									
										
										
										
											2019-07-11 11:59:22 -07:00
										 |  |  |  |         =0 {zero} | 
					
						
							|  |  |  |  |         =2 {{{count}} {name, select, | 
					
						
							|  |  |  |  |                        cat {cats} | 
					
						
							|  |  |  |  |                        dog {dogs} | 
					
						
							|  |  |  |  |                        other {animals} | 
					
						
							|  |  |  |  |                      }!} | 
					
						
							|  |  |  |  |         other {other - {{count}}} | 
					
						
							|  |  |  |  |       }</div>`);
 | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 21:1--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toEqual(`<div>2 animaux<!--nested ICU 0-->!<!--ICU 21:1--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div>autre - 4<!--ICU 21:1--></div>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  |     it('should return the correct plural form for ICU expressions when using "ro" locale', () => { | 
					
						
							|  |  |  |  |       // The "ro" locale has a complex plural function that can handle muliple options
 | 
					
						
							|  |  |  |  |       // (and string inputs)
 | 
					
						
							|  |  |  |  |       //
 | 
					
						
							|  |  |  |  |       // function plural(n: number): number {
 | 
					
						
							|  |  |  |  |       //   let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length;
 | 
					
						
							|  |  |  |  |       //   if (i === 1 && v === 0) return 1;
 | 
					
						
							|  |  |  |  |       //   if (!(v === 0) || n === 0 ||
 | 
					
						
							|  |  |  |  |       //       !(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19)
 | 
					
						
							|  |  |  |  |       //     return 3;
 | 
					
						
							|  |  |  |  |       //   return 5;
 | 
					
						
							|  |  |  |  |       // }
 | 
					
						
							|  |  |  |  |       //
 | 
					
						
							|  |  |  |  |       // Compare this to the "es" locale in the next test
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {no email} =one {one email} =few {a few emails} =other {lots of emails}}')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =0 {no email} =one {one email} =few {a few emails} =other {lots of emails}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       registerLocaleData(localeRo); | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({providers: [{provide: LOCALE_ID, useValue: 'ro'}]}); | 
					
						
							|  |  |  |  |       // We could also use `TestBed.overrideProvider(LOCALE_ID, {useValue: 'ro'});`
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  |           {count, plural, | 
					
						
							|  |  |  |  |             =0 {no email} | 
					
						
							|  |  |  |  |             =one {one email} | 
					
						
							|  |  |  |  |             =few {a few emails} | 
					
						
							|  |  |  |  |             =other {lots of emails} | 
					
						
							|  |  |  |  |           }`);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       // Change detection cycle, no model changes
 | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       fixture.componentInstance.count = 3; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       fixture.componentInstance.count = 1; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       fixture.componentInstance.count = 10; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       fixture.componentInstance.count = 20; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-05-17 16:13:31 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       fixture.componentInstance.count = 0; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-06-03 15:05:34 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  |     it(`should return the correct plural form for ICU expressions when using "es" locale`, () => { | 
					
						
							|  |  |  |  |       // The "es" locale has a simple plural function that can only handle a few options
 | 
					
						
							|  |  |  |  |       // (and not string inputs)
 | 
					
						
							|  |  |  |  |       //
 | 
					
						
							|  |  |  |  |       // function plural(n: number): number {
 | 
					
						
							|  |  |  |  |       //   if (n === 1) return 1;
 | 
					
						
							|  |  |  |  |       //   return 5;
 | 
					
						
							|  |  |  |  |       // }
 | 
					
						
							|  |  |  |  |       //
 | 
					
						
							|  |  |  |  |       // Compare this to the "ro" locale in the previous test
 | 
					
						
							|  |  |  |  |       const icuMessage = '{VAR_PLURAL, plural, =0 {no email} =one ' + | 
					
						
							|  |  |  |  |           '{one email} =few {a few emails} =other {lots of emails}}'; | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId(icuMessage)]: icuMessage}); | 
					
						
							|  |  |  |  |       registerLocaleData(localeEs); | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({providers: [{provide: LOCALE_ID, useValue: 'es'}]}); | 
					
						
							|  |  |  |  |       // We could also use `TestBed.overrideProvider(LOCALE_ID, {useValue: 'es'});`
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |           {count, plural, | 
					
						
							|  |  |  |  |             =0 {no email} | 
					
						
							|  |  |  |  |             =one {one email} | 
					
						
							|  |  |  |  |             =few {a few emails} | 
					
						
							|  |  |  |  |             =other {lots of emails} | 
					
						
							|  |  |  |  |           }`);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       // Change detection cycle, no model changes
 | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 3; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 1; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 10; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 20; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 0; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->'); | 
					
						
							| 
									
										
										
										
											2020-05-03 06:58:51 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('projection', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({selector: 'child', template: '<div><ng-content></ng-content></div>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-06-03 15:05:34 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-03 15:05:34 +02:00
										 |  |  |  |       <child i18n>{ | 
					
						
							|  |  |  |  |         value // i18n(ph = "blah"),
 | 
					
						
							|  |  |  |  |         plural, | 
					
						
							|  |  |  |  |          =1 {one} | 
					
						
							|  |  |  |  |         other {at least {{value}} .} | 
					
						
							|  |  |  |  |       }</child>`
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         value = 3; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toContain('at least'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with empty values', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {} 20 {twenty} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {} 20 {twenty} other {other}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `{count, select, 10 {} 20 {twenty} other {other}}`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       expect(element).toHaveText('other'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('inside a container when creating a view via vcr.createEmbeddedView', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_PLURAL, plural, =1 {ONE} other {OTHER}}')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =1 {ONE} other {OTHER}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Directive({ | 
					
						
							|  |  |  |  |         selector: '[someDir]', | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Dir { | 
					
						
							|  |  |  |  |         constructor( | 
					
						
							|  |  |  |  |             private readonly viewContainerRef: ViewContainerRef, | 
					
						
							|  |  |  |  |             private readonly templateRef: TemplateRef<any>) {} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         ngOnInit() { | 
					
						
							|  |  |  |  |           this.viewContainerRef.createEmbeddedView(this.templateRef); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-cmp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  |               <div *someDir> | 
					
						
							|  |  |  |  |                 <ng-content></ng-content> | 
					
						
							|  |  |  |  |               </div> | 
					
						
							|  |  |  |  |             `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-07 20:46:11 -07:00
										 |  |  |  |             <my-cmp i18n="test" *ngIf="condition">{ | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  |               count, | 
					
						
							|  |  |  |  |               plural, | 
					
						
							|  |  |  |  |               =1 {ONE} | 
					
						
							|  |  |  |  |               other {OTHER} | 
					
						
							|  |  |  |  |             }</my-cmp> | 
					
						
							|  |  |  |  |           `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class App { | 
					
						
							|  |  |  |  |         count = 1; | 
					
						
							|  |  |  |  |         condition = true; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         declarations: [App, Cmp, Dir], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toContain('<my-cmp><div>ONE<!--ICU 21:0--></div><!--container--></my-cmp>'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.count = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toContain('<my-cmp><div>OTHER<!--ICU 21:0--></div><!--container--></my-cmp>'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       // destroy component
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.condition = false; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.textContent).toBe(''); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // render it again and also change ICU case
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.condition = true; | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 1; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |           .toContain('<my-cmp><div>ONE<!--ICU 21:0--></div><!--container--></my-cmp>'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView', | 
					
						
							|  |  |  |  |        () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |          loadTranslations({ | 
					
						
							|  |  |  |  |            [computeMsgId( | 
					
						
							|  |  |  |  |                '{VAR_PLURAL, plural, =1 {ONE} other {{INTERPOLATION} ' + | 
					
						
							|  |  |  |  |                '{VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}')]: | 
					
						
							|  |  |  |  |                '{VAR_PLURAL, plural, =1 {ONE} other {{INTERPOLATION} ' + | 
					
						
							|  |  |  |  |                '{VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}' | 
					
						
							|  |  |  |  |          }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          let dir: Dir|null = null; | 
					
						
							|  |  |  |  |          @Directive({ | 
					
						
							|  |  |  |  |            selector: '[someDir]', | 
					
						
							|  |  |  |  |          }) | 
					
						
							|  |  |  |  |          class Dir { | 
					
						
							|  |  |  |  |            constructor( | 
					
						
							|  |  |  |  |                private readonly viewContainerRef: ViewContainerRef, | 
					
						
							|  |  |  |  |                private readonly templateRef: TemplateRef<any>) { | 
					
						
							|  |  |  |  |              dir = this; | 
					
						
							|  |  |  |  |            } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |            attachEmbeddedView() { | 
					
						
							|  |  |  |  |              this.viewContainerRef.createEmbeddedView(this.templateRef); | 
					
						
							|  |  |  |  |            } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |          @Component({ | 
					
						
							|  |  |  |  |            selector: 'my-cmp', | 
					
						
							|  |  |  |  |            template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  |               <div *someDir> | 
					
						
							|  |  |  |  |                 <ng-content></ng-content> | 
					
						
							|  |  |  |  |               </div> | 
					
						
							|  |  |  |  |             `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          }) | 
					
						
							|  |  |  |  |          class Cmp { | 
					
						
							|  |  |  |  |          } | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          @Component({ | 
					
						
							|  |  |  |  |            selector: 'my-app', | 
					
						
							|  |  |  |  |            template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-11 19:01:49 +02:00
										 |  |  |  |             <my-cmp i18n="test">{ | 
					
						
							|  |  |  |  |               count, | 
					
						
							|  |  |  |  |               plural, | 
					
						
							|  |  |  |  |               =1 {ONE} | 
					
						
							|  |  |  |  |               other {{{count}} {name, select, | 
					
						
							|  |  |  |  |                 cat {cats} | 
					
						
							|  |  |  |  |                 dog {dogs} | 
					
						
							|  |  |  |  |                 other {animals} | 
					
						
							|  |  |  |  |               }!} | 
					
						
							|  |  |  |  |             }</my-cmp> | 
					
						
							|  |  |  |  |           `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          }) | 
					
						
							|  |  |  |  |          class App { | 
					
						
							|  |  |  |  |            count = 1; | 
					
						
							|  |  |  |  |          } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |          TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |            declarations: [App, Cmp, Dir], | 
					
						
							|  |  |  |  |          }); | 
					
						
							|  |  |  |  |          const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |          fixture.componentRef.instance.count = 2; | 
					
						
							|  |  |  |  |          fixture.detectChanges(); | 
					
						
							|  |  |  |  |          expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |              .toBe('<my-cmp><!--container--></my-cmp>'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |          dir!.attachEmbeddedView(); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |          fixture.detectChanges(); | 
					
						
							|  |  |  |  |          expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |              .toBe( | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |                  '<my-cmp><div>2 animals<!--nested ICU 0-->!<!--ICU 21:1--></div><!--container--></my-cmp>'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |          fixture.componentRef.instance.count = 1; | 
					
						
							|  |  |  |  |          fixture.detectChanges(); | 
					
						
							|  |  |  |  |          expect(fixture.debugElement.nativeElement.innerHTML) | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |              .toBe('<my-cmp><div>ONE<!--ICU 21:1--></div><!--container--></my-cmp>'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |        }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with nested containers', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, A {A } B {B } other {C }}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A {A } B {B } other {C }}', | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, A1 {A1 } B1 {B1 } other {C1 }}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A1 {A1 } B1 {B1 } other {C1 }}', | 
					
						
							|  |  |  |  |         [computeMsgId(' {$ICU} ')]: ' {$ICU} ', | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'comp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  |         <ng-container [ngSwitch]="visible"> | 
					
						
							|  |  |  |  |           <ng-container *ngSwitchCase="isVisible()" i18n> | 
					
						
							|  |  |  |  |             {type, select, A { A } B { B } other { C }} | 
					
						
							|  |  |  |  |           </ng-container> | 
					
						
							|  |  |  |  |           <ng-container *ngSwitchCase="!isVisible()" i18n> | 
					
						
							|  |  |  |  |             {type, select, A1 { A1 } B1 { B1 } other { C1 }} | 
					
						
							|  |  |  |  |           </ng-container> | 
					
						
							|  |  |  |  |         </ng-container> | 
					
						
							|  |  |  |  |       `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Comp { | 
					
						
							|  |  |  |  |         type = 'A'; | 
					
						
							|  |  |  |  |         visible = true; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         isVisible() { | 
					
						
							|  |  |  |  |           return true; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       TestBed.configureTestingModule({declarations: [Comp]}); | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = TestBed.createComponent(Comp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       fixture.componentInstance.visible = false; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-06-27 16:10:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A'); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('C1'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('with named interpolations', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A {A - {PH_A}} ' + | 
					
						
							|  |  |  |  |             'B {B - {PH_B}} other {other - {PH_WITH_SPACES}}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A {A (translated) - {PH_A}} ' + | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |             'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}', | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'comp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  |           <ng-container i18n>{ | 
					
						
							|  |  |  |  |             type, | 
					
						
							|  |  |  |  |             select, | 
					
						
							|  |  |  |  |               A {A - {{ typeA // i18n(ph="PH_A") }}}
 | 
					
						
							|  |  |  |  |               B {B - {{ typeB // i18n(ph="PH_B") }}}
 | 
					
						
							|  |  |  |  |               other {other - {{ typeC // i18n(ph="PH WITH SPACES") }}}
 | 
					
						
							|  |  |  |  |           }</ng-container> | 
					
						
							|  |  |  |  |         `,
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Comp { | 
					
						
							|  |  |  |  |         type = 'A'; | 
					
						
							|  |  |  |  |         typeA = 'Type A'; | 
					
						
							|  |  |  |  |         typeB = 'Type B'; | 
					
						
							|  |  |  |  |         typeC = 'Type C'; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       TestBed.configureTestingModule({declarations: [Comp]}); | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = TestBed.createComponent(Comp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('A (translated) - Type A'); | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       fixture.componentInstance.type = 'C';  // trigger "other" case
 | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-07-08 17:37:26 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A (translated) - Type A'); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('other (translated) - Type C'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-22 20:55:07 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('should work inside an ngTemplateOutlet inside an ngFor', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, A {A } B {B } other {other - {PH_WITH_SPACES}}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A {A } B {B } other {other - {PH_WITH_SPACES}}}', | 
					
						
							|  |  |  |  |         [computeMsgId('{$ICU} ')]: '{$ICU} ' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-07-22 20:55:07 -07:00
										 |  |  |  |           <ng-template #myTemp i18n let-type>{ | 
					
						
							|  |  |  |  |             type, | 
					
						
							|  |  |  |  |             select, | 
					
						
							|  |  |  |  |             A {A } | 
					
						
							|  |  |  |  |             B {B } | 
					
						
							| 
									
										
										
										
											2019-08-02 12:42:04 +01:00
										 |  |  |  |             other {other - {{ typeC // i18n(ph="PH WITH SPACES") }}}
 | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-22 20:55:07 -07:00
										 |  |  |  |           </ng-template> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           <div *ngFor="let type of types"> | 
					
						
							|  |  |  |  |             <ng-container *ngTemplateOutlet="myTemp; context: {$implicit: type}"> | 
					
						
							|  |  |  |  |             </ng-container> | 
					
						
							|  |  |  |  |           </div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |         types = ['A', 'B', 'C']; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [AppComponent]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('B'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-10-15 15:54:14 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should use metadata from container element if a message is a single ICU', () => { | 
					
						
							|  |  |  |  |       loadTranslations({idA: '{VAR_SELECT, select, 1 {un} other {plus d\'un}}'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <div i18n="@@idA">{count, select, 1 {one} other {more than one}}</div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |         count = 2; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [AppComponent]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.nativeElement.innerHTML).toContain('plus d\'un'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-11-25 16:39:31 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should support ICUs without "other" cases', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         idA: '{VAR_SELECT, select, 1 {un (select)} 2 {deux (select)}}', | 
					
						
							|  |  |  |  |         idB: '{VAR_PLURAL, plural, =1 {un (plural)} =2 {deux (plural)}}', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <div i18n="@@idA">{count, select, 1 {one (select)} 2 {two (select)}}</div> - | 
					
						
							|  |  |  |  |           <div i18n="@@idB">{count, plural, =1 {one (plural)} =2 {two (plural)}}</div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |         count = 1; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [AppComponent]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe('un (select) - un (plural)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 3; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // there is no ICU case for count=3
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('-'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // there is no ICU case for count=4, making sure content is still empty
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('-'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // check switching to an existing case after processing an ICU without matching case
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('deux (select) - deux (plural)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 1; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // check that we can go back to the first ICU case
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe('un (select) - un (plural)'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should support nested ICUs without "other" cases', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         idA: '{VAR_SELECT_1, select, A {{VAR_SELECT, select, ' + | 
					
						
							|  |  |  |  |             '1 {un (select)} 2 {deux (select)}}} other {}}', | 
					
						
							|  |  |  |  |         idB: '{VAR_SELECT, select, A {{VAR_PLURAL, plural, ' + | 
					
						
							|  |  |  |  |             '=1 {un (plural)} =2 {deux (plural)}}} other {}}', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <div i18n="@@idA">{ | 
					
						
							|  |  |  |  |             type, select, | 
					
						
							|  |  |  |  |               A {{count, select, 1 {one (select)} 2 {two (select)}}} | 
					
						
							|  |  |  |  |               other {} | 
					
						
							|  |  |  |  |           }</div> - | 
					
						
							|  |  |  |  |           <div i18n="@@idB">{ | 
					
						
							|  |  |  |  |             type, select, | 
					
						
							|  |  |  |  |               A {{count, plural, =1 {one (plural)} =2 {two (plural)}}} | 
					
						
							|  |  |  |  |               other {} | 
					
						
							|  |  |  |  |           }</div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |         type = 'A'; | 
					
						
							|  |  |  |  |         count = 1; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [AppComponent]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe('un (select) - un (plural)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 3; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // there is no case for count=3 in nested ICU
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('-'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 4; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // there is no case for count=4 in nested ICU, making sure content is still empty
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('-'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       // check switching to an existing case after processing nested ICU without matching
 | 
					
						
							|  |  |  |  |       // case
 | 
					
						
							| 
									
										
										
										
											2019-11-25 16:39:31 -08:00
										 |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('deux (select) - deux (plural)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count = 1; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // check that we can go back to the first case in nested ICU
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe('un (select) - un (plural)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.type = 'B'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // check that nested ICU is removed if root ICU case has changed
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('-'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-12-02 13:58:32 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should support ICUs with pipes', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         idA: '{VAR_SELECT, select, 1 {{INTERPOLATION} article} 2 {deux articles}}', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <div i18n="@@idA">{count$ | async, select, 1 {{{count$ | async}} item} 2 {two items}}</div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |         count$ = new BehaviorSubject<number>(1); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         imports: [CommonModule], | 
					
						
							|  |  |  |  |         declarations: [AppComponent], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe('1 article'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count$.next(3); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // there is no ICU case for count=3, expecting empty content
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe(''); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.count$.next(2); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       // checking the second ICU case
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('deux articles'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |     it('should handle select expressions without an `other` parameter inside a template', () => { | 
					
						
							| 
									
										
										
										
											2020-08-04 12:42:12 -07:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <ng-container *ngFor="let item of items">{item.value, select, 0 {A} 1 {B} 2 {C}}</ng-container> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items = [{value: 0}, {value: 1}, {value: 1337}]; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('AB'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items[0].value = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('CB'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should render an element whose case did not match initially', () => { | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <p *ngFor="let item of items">{item.value, select, 0 {A} 1 {B} 2 {C}}</p> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items = [{value: 0}, {value: 1}, {value: 1337}]; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('AB'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items[2].value = 2; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('ABC'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should remove an element whose case matched initially, but does not anymore', () => { | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <p *ngFor="let item of items">{item.value, select, 0 {A} 1 {B} 2 {C}}</p> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items = [{value: 0}, {value: 1}]; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('AB'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.items[0].value = 1337; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent.trim()).toBe('B'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   describe('should support attributes', () => { | 
					
						
							|  |  |  |  |     it('text', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('text')]: 'texte'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `<div i18n i18n-title title='text'></div>`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div title="texte"></div>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('interpolations', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<div i18n i18n-title title="hello {{name}}"></div>`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div title="bonjour Angular"></div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'John'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div title="bonjour John"></div>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with pipes', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |           AppComp, `<div i18n i18n-title title="hello {{name | uppercase}}"></div>`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div title="bonjour ANGULAR"></div>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('multiple attributes', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |           AppComp, | 
					
						
							|  |  |  |  |           `<input i18n i18n-title title="hello {{name}}" i18n-placeholder placeholder="hello {{name}}">`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<input title="bonjour Angular" placeholder="bonjour Angular">`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'John'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<input title="bonjour John" placeholder="bonjour John">`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('on removed elements', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('text')]: 'texte', | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}content{$CLOSE_TAG_SPAN}')]: 'contenu', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<div i18n><span i18n-title title="text">content</span></div>`); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<div>contenu</div>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with custom interpolation config', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Hello {$INTERPOLATION}', 'm')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       const interpolation = ['{%', '%}'] as [string, string]; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       TestBed.overrideComponent(AppComp, {set: {interpolation}}); | 
					
						
							|  |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<div i18n-title="m|d" title="Hello {% name %}"></div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement.firstChild; | 
					
						
							|  |  |  |  |       expect(element.title).toBe('Bonjour Angular'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('in nested template', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Item {$INTERPOLATION}', 'm')]: 'Article {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |           <div *ngFor='let item of [1,2,3]'> | 
					
						
							|  |  |  |  |             <div i18n-title='m|d' title='Item {{ item }}'></div> | 
					
						
							|  |  |  |  |           </div>`);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |       for (let i = 0; i < element.children.length; i++) { | 
					
						
							|  |  |  |  |         const child = element.children[i]; | 
					
						
							|  |  |  |  |         expect((child as any).innerHTML).toBe(`<div title="Article ${i + 1}"></div>`); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should add i18n attributes on self-closing tags', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = | 
					
						
							|  |  |  |  |           initWithTemplate(AppComp, `<img src="logo.png" i18n-title title="Hello {{ name }}">`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const element = fixture.nativeElement.firstChild; | 
					
						
							|  |  |  |  |       expect(element.title).toBe('Bonjour Angular'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 22:59:34 -08:00
										 |  |  |  |     it('should process i18n attributes on explicit <ng-template> elements', () => { | 
					
						
							|  |  |  |  |       const titleDirInstances: TitleDir[] = []; | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Hello')]: 'Bonjour'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Directive({ | 
					
						
							|  |  |  |  |         selector: '[title]', | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class TitleDir { | 
					
						
							|  |  |  |  |         @Input() title = ''; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         constructor() { | 
					
						
							|  |  |  |  |           titleDirInstances.push(this); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-02-25 22:59:34 -08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'comp', | 
					
						
							|  |  |  |  |         template: '<ng-template i18n-title title="Hello"></ng-template>', | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Comp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         declarations: [Comp, TitleDir], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Comp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // make sure we only match `TitleDir` once
 | 
					
						
							|  |  |  |  |       expect(titleDirInstances.length).toBe(1); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(titleDirInstances[0].title).toBe('Bonjour'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should match directive only once in case i18n attrs are present on inline template', () => { | 
					
						
							|  |  |  |  |       const titleDirInstances: TitleDir[] = []; | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Hello')]: 'Bonjour'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Directive({selector: '[title]'}) | 
					
						
							|  |  |  |  |       class TitleDir { | 
					
						
							|  |  |  |  |         @Input() title: string = ''; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         constructor(public elRef: ElementRef) { | 
					
						
							|  |  |  |  |           titleDirInstances.push(this); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-02-25 22:59:34 -08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-cmp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <button *ngIf="true" i18n-title title="Hello"></button> | 
					
						
							|  |  |  |  |         `,
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         imports: [CommonModule], | 
					
						
							|  |  |  |  |         declarations: [Cmp, TitleDir], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Cmp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // make sure we only match `TitleDir` once and on the right element
 | 
					
						
							|  |  |  |  |       expect(titleDirInstances.length).toBe(1); | 
					
						
							|  |  |  |  |       expect(titleDirInstances[0].elRef.nativeElement instanceof HTMLButtonElement).toBeTruthy(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(titleDirInstances[0].title).toBe('Bonjour'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 22:12:29 -08:00
										 |  |  |  |     it('should support static i18n attributes on inline templates', () => { | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Hello')]: 'Bonjour'}); | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-cmp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <div *ngIf="true" i18n-title title="Hello"></div> | 
					
						
							|  |  |  |  |         `,
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         imports: [CommonModule], | 
					
						
							|  |  |  |  |         declarations: [Cmp], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Cmp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.firstChild.title).toBe('Bonjour'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-09 21:49:02 -07:00
										 |  |  |  |     it('should allow directive inputs (as an interpolated prop) on <ng-template>', () => { | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       let dirInstance: WithInput; | 
					
						
							|  |  |  |  |       @Directive({selector: '[dir]'}) | 
					
						
							|  |  |  |  |       class WithInput { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         constructor() { | 
					
						
							|  |  |  |  |           dirInstance = this; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-03-09 21:49:02 -07:00
										 |  |  |  |         @Input() dir: string = ''; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-app', | 
					
						
							|  |  |  |  |         template: '<ng-template i18n-dir dir="Hello {{ name }}"></ng-template>', | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class TestComp { | 
					
						
							|  |  |  |  |         name = 'Angular'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [TestComp, WithInput]}); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(TestComp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       expect(dirInstance!.dir).toBe('Bonjour Angular'); | 
					
						
							| 
									
										
										
										
											2020-03-09 21:49:02 -07:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should allow directive inputs (as interpolated props)' + | 
					
						
							|  |  |  |  |            'on <ng-template> with structural directives present', | 
					
						
							|  |  |  |  |        () => { | 
					
						
							|  |  |  |  |          loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |          let dirInstance: WithInput; | 
					
						
							|  |  |  |  |          @Directive({selector: '[dir]'}) | 
					
						
							|  |  |  |  |          class WithInput { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |            constructor() { | 
					
						
							|  |  |  |  |              dirInstance = this; | 
					
						
							|  |  |  |  |            } | 
					
						
							| 
									
										
										
										
											2020-03-09 21:49:02 -07:00
										 |  |  |  |            @Input() dir: string = ''; | 
					
						
							|  |  |  |  |          } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |          @Component({ | 
					
						
							|  |  |  |  |            selector: 'my-app', | 
					
						
							|  |  |  |  |            template: '<ng-template *ngIf="true" i18n-dir dir="Hello {{ name }}"></ng-template>', | 
					
						
							|  |  |  |  |          }) | 
					
						
							|  |  |  |  |          class TestComp { | 
					
						
							|  |  |  |  |            name = 'Angular'; | 
					
						
							|  |  |  |  |          } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |          TestBed.configureTestingModule({declarations: [TestComp, WithInput]}); | 
					
						
							|  |  |  |  |          const fixture = TestBed.createComponent(TestComp); | 
					
						
							|  |  |  |  |          fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |          expect(dirInstance!.dir).toBe('Bonjour Angular'); | 
					
						
							| 
									
										
										
										
											2020-03-09 21:49:02 -07:00
										 |  |  |  |        }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     it('should apply i18n attributes during second template pass', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({[computeMsgId('Set')]: 'Set'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Directive({ | 
					
						
							|  |  |  |  |         selector: '[test]', | 
					
						
							|  |  |  |  |         inputs: ['test'], | 
					
						
							|  |  |  |  |         exportAs: 'dir', | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Dir { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'other', | 
					
						
							|  |  |  |  |         template: `<div i18n #ref="dir" test="Set" i18n-test="This is also a test"></div>` | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Other { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'blah', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-05 15:38:36 +02:00
										 |  |  |  |           <other></other> | 
					
						
							|  |  |  |  |           <other></other> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         declarations: [Dir, Cmp, Other], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Cmp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.children[0].children[0].references.ref.test).toBe('Set'); | 
					
						
							|  |  |  |  |       expect(fixture.debugElement.children[1].children[0].references.ref.test).toBe('Set'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('with complex expressions', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2}')]: | 
					
						
							|  |  |  |  |             '{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr)' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-08-24 14:02:30 -07:00
										 |  |  |  |         <div i18n-title title="{{ name | uppercase }} - {{ obj?.a?.b }} - {{ obj?.getA()?.b }}"></div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |       // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty
 | 
					
						
							|  |  |  |  |       // strings
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR -  -  (fr)`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.obj = { | 
					
						
							|  |  |  |  |         a: {b: 'value 1'}, | 
					
						
							|  |  |  |  |         getA: () => ({b: 'value 2'}), | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - value 1 - value 2 (fr)`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-10-03 16:47:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 11:12:27 -08:00
										 |  |  |  |     it('should support i18n attributes on <ng-container> elements', () => { | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Hello', 'meaning')]: 'Bonjour'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Directive({selector: '[mydir]'}) | 
					
						
							|  |  |  |  |       class Dir { | 
					
						
							|  |  |  |  |         @Input() mydir: string = ''; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-cmp', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <ng-container i18n-mydir="meaning|description" mydir="Hello"></ng-container> | 
					
						
							|  |  |  |  |         `,
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Cmp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({ | 
					
						
							|  |  |  |  |         declarations: [Cmp, Dir], | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Cmp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const dir = fixture.debugElement.childNodes[0].injector.get(Dir); | 
					
						
							|  |  |  |  |       expect(dir.mydir).toEqual('Bonjour'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 17:59:36 -07:00
										 |  |  |  |   describe('empty translations', () => { | 
					
						
							|  |  |  |  |     it('should replace existing text content with empty translation', () => { | 
					
						
							|  |  |  |  |       loadTranslations({[computeMsgId('Some Text')]: ''}); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, '<div i18n>Some Text</div>'); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe(''); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should replace existing DOM elements with empty translation', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             ' Start {$START_TAG_DIV}DIV{$CLOSE_TAG_DIV}' + | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}SPAN{$CLOSE_TAG_SPAN} End ')]: '', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <div i18n> | 
					
						
							|  |  |  |  |           Start | 
					
						
							|  |  |  |  |           <div>DIV</div> | 
					
						
							|  |  |  |  |           <span>SPAN</span> | 
					
						
							|  |  |  |  |           End | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe(''); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should replace existing ICU content with empty translation', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_PLURAL, plural, =0 {zero} other {more than zero}}')]: '', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <div i18n>{count, plural, =0 {zero} other {more than zero}}</div> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe(''); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   it('should work with directives and host bindings', () => { | 
					
						
							|  |  |  |  |     let directiveInstances: ClsDir[] = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Directive({selector: '[test]'}) | 
					
						
							|  |  |  |  |     class ClsDir { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       @HostBinding('className') klass = 'foo'; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       constructor() { | 
					
						
							|  |  |  |  |         directiveInstances.push(this); | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       selector: `my-app`, | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  |       <div i18n test i18n-title title="start {{exp1}} middle {{exp2}} end" outer> | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |          trad: {exp1, plural, | 
					
						
							|  |  |  |  |               =0 {no <b title="none">emails</b>!} | 
					
						
							|  |  |  |  |               =1 {one <i>email</i>} | 
					
						
							|  |  |  |  |               other {{{exp1}} emails} | 
					
						
							|  |  |  |  |          } | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  |       </div><div test inner></div>`
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |       exp1 = 1; | 
					
						
							|  |  |  |  |       exp2 = 2; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [ClsDir, MyApp]}); | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       // Note that this translation switches the order of the expressions!
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId('start {$INTERPOLATION} middle {$INTERPOLATION_1} end')]: | 
					
						
							|  |  |  |  |           'début {$INTERPOLATION_1} milieu {$INTERPOLATION} fin', | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |           '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}', | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId(' trad: {$ICU} ')]: ' traduction: {$ICU} ' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     const outerDiv: HTMLElement = fixture.nativeElement.querySelector('div[outer]'); | 
					
						
							|  |  |  |  |     const innerDiv: HTMLElement = fixture.nativeElement.querySelector('div[inner]'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Note that ideally we'd just compare the innerHTML here, but different browsers return
 | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  |     // the order of attributes differently. E.g. most browsers preserve the declaration
 | 
					
						
							|  |  |  |  |     // order, but IE does not.
 | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  |     expect(outerDiv.getAttribute('title')).toBe('début 2 milieu 1 fin'); | 
					
						
							|  |  |  |  |     expect(outerDiv.getAttribute('class')).toBe('foo'); | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |     expect(outerDiv.textContent!.trim()).toBe('traduction: un email'); | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  |     expect(innerDiv.getAttribute('class')).toBe('foo'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     directiveInstances.forEach(instance => instance.klass = 'bar'); | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.exp1 = 2; | 
					
						
							|  |  |  |  |     fixture.componentRef.instance.exp2 = 3; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(outerDiv.getAttribute('title')).toBe('début 3 milieu 2 fin'); | 
					
						
							|  |  |  |  |     expect(outerDiv.getAttribute('class')).toBe('bar'); | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |     expect(outerDiv.textContent!.trim()).toBe('traduction: 2 emails'); | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  |     expect(innerDiv.getAttribute('class')).toBe('bar'); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should handle i18n attribute with directive inputs', () => { | 
					
						
							|  |  |  |  |     let calledTitle = false; | 
					
						
							|  |  |  |  |     let calledValue = false; | 
					
						
							|  |  |  |  |     @Component({selector: 'my-comp', template: ''}) | 
					
						
							|  |  |  |  |     class MyComp { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       t!: string; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Input() | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       get title() { | 
					
						
							|  |  |  |  |         return this.t; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       set title(title) { | 
					
						
							|  |  |  |  |         calledTitle = true; | 
					
						
							|  |  |  |  |         this.t = title; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Input() | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       get value() { | 
					
						
							|  |  |  |  |         return this.val; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       set value(value: string) { | 
					
						
							|  |  |  |  |         calledValue = true; | 
					
						
							|  |  |  |  |         this.val = value; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       val!: string; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [AppComp, MyComp]}); | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}', | 
					
						
							|  |  |  |  |       [computeMsgId('works')]: 'fonctionne', | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate( | 
					
						
							|  |  |  |  |         AppComp, | 
					
						
							|  |  |  |  |         `<my-comp i18n i18n-title title="works" i18n-value="hi" value="Hello {{name}}"></my-comp>`); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const directive = fixture.debugElement.children[0].injector.get(MyComp); | 
					
						
							|  |  |  |  |     expect(calledValue).toEqual(true); | 
					
						
							|  |  |  |  |     expect(calledTitle).toEqual(true); | 
					
						
							|  |  |  |  |     expect(directive.value).toEqual(`Bonjour Angular`); | 
					
						
							|  |  |  |  |     expect(directive.title).toEqual(`fonctionne`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support adding/moving/removing nodes', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV6}{$CLOSE_TAG_DIV6}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}')]: '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}Bonjour monde' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}' + | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         <div2></div2> | 
					
						
							|  |  |  |  |         <div3></div3> | 
					
						
							|  |  |  |  |         <div4></div4> | 
					
						
							|  |  |  |  |         <div5></div5> | 
					
						
							|  |  |  |  |         <div6></div6> | 
					
						
							|  |  |  |  |         <div7></div7> | 
					
						
							|  |  |  |  |         <div8></div8> | 
					
						
							|  |  |  |  |       </div>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             `<div><div2></div2><div8></div8><div4></div4><div5></div5>Bonjour monde<div3></div3><div7></div7></div>`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   describe('projection', () => { | 
					
						
							|  |  |  |  |     it('should project the translations', () => { | 
					
						
							|  |  |  |  |       @Component({selector: 'child', template: '<p><ng-content></ng-content></p>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |             <div i18n> | 
					
						
							|  |  |  |  |               <child>I am projected from | 
					
						
							|  |  |  |  |                 <b i18n-title title="Child of {{name}}">{{name}}<remove-me-1></remove-me-1></b> | 
					
						
							|  |  |  |  |                 <remove-me-2></remove-me-2> | 
					
						
							|  |  |  |  |               </child> | 
					
						
							|  |  |  |  |               <remove-me-3></remove-me-3> | 
					
						
							|  |  |  |  |             </div>`
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Child of {$INTERPOLATION}')]: 'Enfant de {$INTERPOLATION}', | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_CHILD}I am projected from' + | 
					
						
							|  |  |  |  |             ' {$START_BOLD_TEXT}{$INTERPOLATION}{$START_TAG_REMOVE_ME_1}{$CLOSE_TAG_REMOVE_ME_1}{$CLOSE_BOLD_TEXT}' + | 
					
						
							|  |  |  |  |             '{$START_TAG_REMOVE_ME_2}{$CLOSE_TAG_REMOVE_ME_2}' + | 
					
						
							|  |  |  |  |             '{$CLOSE_TAG_CHILD}' + | 
					
						
							|  |  |  |  |             '{$START_TAG_REMOVE_ME_3}{$CLOSE_TAG_REMOVE_ME_3}')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             '{$START_TAG_CHILD}Je suis projeté depuis {$START_BOLD_TEXT}{$INTERPOLATION}{$CLOSE_BOLD_TEXT}{$CLOSE_TAG_CHILD}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               `<div><child><p>Je suis projeté depuis <b title="Enfant de Parent">Parent</b></p></child></div>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project a translated i18n block', () => { | 
					
						
							|  |  |  |  |       @Component({selector: 'child', template: '<p><ng-content></ng-content></p>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |           <div> | 
					
						
							|  |  |  |  |             <child> | 
					
						
							|  |  |  |  |               <any></any> | 
					
						
							|  |  |  |  |               <b i18n i18n-title title="Child of {{name}}">I am projected from {{name}}</b> | 
					
						
							|  |  |  |  |               <any></any> | 
					
						
							|  |  |  |  |             </child> | 
					
						
							|  |  |  |  |           </div>`
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Child of {$INTERPOLATION}')]: 'Enfant de {$INTERPOLATION}', | 
					
						
							|  |  |  |  |         [computeMsgId('I am projected from {$INTERPOLATION}')]: | 
					
						
							|  |  |  |  |             'Je suis projeté depuis {$INTERPOLATION}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               `<div><child><p><any></any><b title="Enfant de Parent">Je suis projeté depuis Parent</b><any></any></p></child></div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // it should be able to render a new component with the same template code
 | 
					
						
							|  |  |  |  |       const fixture2 = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture2.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(fixture2.nativeElement.innerHTML); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture2.componentRef.instance.name = 'Parent 2'; | 
					
						
							|  |  |  |  |       fixture2.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture2.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               `<div><child><p><any></any><b title="Enfant de Parent 2">Je suis projeté depuis Parent 2</b><any></any></p></child></div>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // The first fixture should not have changed
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).not.toEqual(fixture2.nativeElement.innerHTML); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should re-project translations when multiple projections', () => { | 
					
						
							|  |  |  |  |       @Component({selector: 'grand-child', template: '<div><ng-content></ng-content></div>'}) | 
					
						
							|  |  |  |  |       class GrandChild { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component( | 
					
						
							|  |  |  |  |           {selector: 'child', template: '<grand-child><ng-content></ng-content></grand-child>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child i18n><b>Hello</b> World!</child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_BOLD_TEXT}Hello{$CLOSE_BOLD_TEXT} World!')]: | 
					
						
							|  |  |  |  |             '{$START_BOLD_TEXT}Bonjour{$CLOSE_BOLD_TEXT} monde!' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual('<child><grand-child><div><b>Bonjour</b> monde!</div></grand-child></child>'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should be able to remove projected placeholders', () => { | 
					
						
							|  |  |  |  |       @Component({selector: 'grand-child', template: '<div><ng-content></ng-content></div>'}) | 
					
						
							|  |  |  |  |       class GrandChild { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component( | 
					
						
							|  |  |  |  |           {selector: 'child', template: '<grand-child><ng-content></ng-content></grand-child>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child i18n><b>Hello</b> World!</child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations( | 
					
						
							|  |  |  |  |           {[computeMsgId('{$START_BOLD_TEXT}Hello{$CLOSE_BOLD_TEXT} World!')]: 'Bonjour monde!'}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual('<child><grand-child><div>Bonjour monde!</div></grand-child></child>'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project translations with selectors', () => { | 
					
						
							|  |  |  |  |       @Component({selector: 'child', template: `<ng-content select='span'></ng-content>`}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |           <child i18n> | 
					
						
							|  |  |  |  |             <span title="keepMe"></span> | 
					
						
							|  |  |  |  |             <span title="deleteMe"></span> | 
					
						
							|  |  |  |  |           </child> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1}{$CLOSE_TAG_SPAN}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}Contenu{$CLOSE_TAG_SPAN}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual('<child><span title="keepMe">Contenu</span></child>'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project content in i18n blocks', () => { | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'child', | 
					
						
							|  |  |  |  |         template: `<div i18n>Content projected from <ng-content></ng-content></div>` | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child>{{name}}</child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |             'Contenu projeté depuis {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<child><div>Contenu projeté depuis Parent</div></child>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'Parent component'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<child><div>Contenu projeté depuis Parent component</div></child>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project content in i18n blocks with placeholders', () => { | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'child', | 
					
						
							|  |  |  |  |         template: `<div i18n>Content projected from <ng-content></ng-content></div>` | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child><b>{{name}}</b></child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT} a projeté le contenu' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<child><div><b>Parent</b> a projeté le contenu</div></child>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project translated content in i18n blocks', () => { | 
					
						
							|  |  |  |  |       @Component( | 
					
						
							|  |  |  |  |           {selector: 'child', template: `<div i18n>Child content <ng-content></ng-content></div>`}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child i18n>and projection from {{name}}</child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: | 
					
						
							|  |  |  |  |             'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', | 
					
						
							|  |  |  |  |         [computeMsgId('and projection from {$INTERPOLATION}')]: | 
					
						
							|  |  |  |  |             'et projection depuis {$INTERPOLATION}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual(`<child><div>Contenu enfant et projection depuis Parent</div></child>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project bare ICU expressions', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}')]: | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({selector: 'child', template: '<div><ng-content></ng-content></div>'}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-05-31 17:11:57 +02:00
										 |  |  |  |       <child i18n>{ | 
					
						
							|  |  |  |  |         value // i18n(ph = "blah"),
 | 
					
						
							|  |  |  |  |         plural, | 
					
						
							|  |  |  |  |          =1 {one} | 
					
						
							|  |  |  |  |         other {at least {{value}} .} | 
					
						
							|  |  |  |  |       }</child>`
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         value = 3; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toContain('at least'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should project ICUs in i18n blocks', () => { | 
					
						
							|  |  |  |  |       @Component( | 
					
						
							|  |  |  |  |           {selector: 'child', template: `<div i18n>Child content <ng-content></ng-content></div>`}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'parent', | 
					
						
							|  |  |  |  |         template: | 
					
						
							|  |  |  |  |             `<child i18n>and projection from {name, select, angular {Angular} other {{{name}}}}</child>` | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('{VAR_SELECT, select, angular {Angular} other {{INTERPOLATION}}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, angular {Angular} other {{INTERPOLATION}}}', | 
					
						
							|  |  |  |  |         [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: | 
					
						
							|  |  |  |  |             'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', | 
					
						
							|  |  |  |  |         [computeMsgId('and projection from {$ICU}')]: 'et projection depuis {$ICU}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<child><div>Contenu enfant et projection depuis Parent<!--ICU 21:0--></div></child>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       fixture.componentRef.instance.name = 'angular'; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML) | 
					
						
							|  |  |  |  |           .toEqual( | 
					
						
							| 
									
										
										
										
											2020-10-13 22:00:43 -07:00
										 |  |  |  |               `<child><div>Contenu enfant et projection depuis Angular<!--ICU 21:0--></div></child>`); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it(`shouldn't project deleted projections in i18n blocks`, () => { | 
					
						
							|  |  |  |  |       @Component( | 
					
						
							|  |  |  |  |           {selector: 'child', template: `<div i18n>Child content <ng-content></ng-content></div>`}) | 
					
						
							|  |  |  |  |       class Child { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'parent', template: `<child i18n>and projection from {{name}}</child>`}) | 
					
						
							|  |  |  |  |       class Parent { | 
					
						
							|  |  |  |  |         name: string = 'Parent'; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [Parent, Child]}); | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |         [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: | 
					
						
							|  |  |  |  |             'Contenu enfant', | 
					
						
							|  |  |  |  |         [computeMsgId('and projection from {$INTERPOLATION}')]: | 
					
						
							|  |  |  |  |             'et projection depuis {$INTERPOLATION}' | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(Parent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.innerHTML).toEqual(`<child><div>Contenu enfant</div></child>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should display/destroy projected i18n content', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, A {A} B {B} other {other}}')]: | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, A {A} B {B} other {other}}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-07 20:46:11 -07:00
										 |  |  |  |             <ng-container>(<ng-content></ng-content>)</ng-container> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class MyContentApp { | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-06-07 20:46:11 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'my-app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							| 
									
										
										
										
											2019-06-07 20:46:11 -07:00
										 |  |  |  |           <app i18n *ngIf="condition">{type, select, A {A} B {B} other {other}}</app> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       }) | 
					
						
							|  |  |  |  |       class MyApp { | 
					
						
							|  |  |  |  |         type = 'A'; | 
					
						
							|  |  |  |  |         condition = true; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [MyApp, MyContentApp]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toContain('(A)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // change `condition` to remove <app>
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.condition = false; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // should not contain 'A'
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toBe(''); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // display <app> again
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.type = 'B'; | 
					
						
							|  |  |  |  |       fixture.componentInstance.condition = true; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // expect that 'B' is now displayed
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toContain('(B)'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   describe('queries', () => { | 
					
						
							|  |  |  |  |     function toHtml(element: Element): string { | 
					
						
							|  |  |  |  |       return element.innerHTML.replace(/\sng-reflect-\S*="[^"]*"/g, '') | 
					
						
							|  |  |  |  |           .replace(/<!--bindings=\{(\W.*\W\s*)?\}-->/g, ''); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('detached nodes should still be part of query', () => { | 
					
						
							|  |  |  |  |       @Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'}) | 
					
						
							|  |  |  |  |       class TextDirective { | 
					
						
							|  |  |  |  |         // TODO(issue/24571): remove '!'.
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         text!: string; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |         constructor() {} | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({selector: 'div-query', template: '<ng-container #vc></ng-container>'}) | 
					
						
							|  |  |  |  |       class DivQuery { | 
					
						
							|  |  |  |  |         // TODO(issue/24571): remove '!'.
 | 
					
						
							|  |  |  |  |         @ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // TODO(issue/24571): remove '!'.
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         @ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         // TODO(issue/24571): remove '!'.
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         @ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         create() { | 
					
						
							|  |  |  |  |           this.vc.createEmbeddedView(this.template); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |         destroy() { | 
					
						
							|  |  |  |  |           this.vc.clear(); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId( | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_TEMPLATE}{$START_TAG_DIV_1}' + | 
					
						
							|  |  |  |  |             '{$START_TAG_DIV}' + | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}Content{$CLOSE_TAG_SPAN}' + | 
					
						
							|  |  |  |  |             '{$CLOSE_TAG_DIV}' + | 
					
						
							|  |  |  |  |             '{$CLOSE_TAG_DIV}{$CLOSE_TAG_NG_TEMPLATE}')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_TEMPLATE}Contenu{$CLOSE_TAG_NG_TEMPLATE}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  |           <div-query #q i18n> | 
					
						
							|  |  |  |  |             <ng-template> | 
					
						
							|  |  |  |  |               <div> | 
					
						
							|  |  |  |  |                 <div *ngIf="visible"> | 
					
						
							|  |  |  |  |                   <span text="1">Content</span> | 
					
						
							|  |  |  |  |                 </div> | 
					
						
							|  |  |  |  |               </div> | 
					
						
							|  |  |  |  |             </ng-template> | 
					
						
							|  |  |  |  |           </div-query> | 
					
						
							|  |  |  |  |         `);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |       const q = fixture.debugElement.children[0].references.q; | 
					
						
							|  |  |  |  |       expect(q.query.length).toEqual(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // Create embedded view
 | 
					
						
							|  |  |  |  |       q.create(); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(q.query.length).toEqual(1); | 
					
						
							|  |  |  |  |       expect(toHtml(fixture.nativeElement)) | 
					
						
							|  |  |  |  |           .toEqual(`<div-query>Contenu<!--ng-container--></div-query>`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // Disable ng-if
 | 
					
						
							|  |  |  |  |       fixture.componentInstance.visible = false; | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(q.query.length).toEqual(0); | 
					
						
							|  |  |  |  |       expect(toHtml(fixture.nativeElement)) | 
					
						
							|  |  |  |  |           .toEqual(`<div-query>Contenu<!--ng-container--></div-query>`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 16:33:09 -08:00
										 |  |  |  |   describe('invalid translations handling', () => { | 
					
						
							|  |  |  |  |     it('should throw in case invalid ICU is present in a template', () => { | 
					
						
							|  |  |  |  |       // Error message is produced by Compiler.
 | 
					
						
							|  |  |  |  |       expect(() => initWithTemplate(AppComp, '{count, select, 10 {ten} other {other}')) | 
					
						
							|  |  |  |  |           .toThrowError( | 
					
						
							|  |  |  |  |               /Invalid ICU message. Missing '}'. \("{count, select, 10 {ten} other {other}\[ERROR ->\]"\)/); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should throw in case invalid ICU is present in translation', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{VAR_SELECT, select, 10 {ten} other {other}}')]: | 
					
						
							|  |  |  |  |             // Missing "}" at the end of translation.
 | 
					
						
							|  |  |  |  |             '{VAR_SELECT, select, 10 {dix} other {autre}' | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // Error message is produced at runtime.
 | 
					
						
							|  |  |  |  |       expect(() => initWithTemplate(AppComp, '{count, select, 10 {ten} other {other}}')) | 
					
						
							|  |  |  |  |           .toThrowError( | 
					
						
							|  |  |  |  |               /Unable to parse ICU expression in "{<7B>0<EFBFBD>, select, 10 {dix} other {autre}" message./); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should throw in case unescaped curly braces are present in a template', () => { | 
					
						
							|  |  |  |  |       // Error message is produced by Compiler.
 | 
					
						
							|  |  |  |  |       expect(() => initWithTemplate(AppComp, 'Text { count }')) | 
					
						
							|  |  |  |  |           .toThrowError( | 
					
						
							|  |  |  |  |               /Do you have an unescaped "{" in your template\? Use "{{ '{' }}"\) to escape it/); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should throw in case curly braces are added into translation', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         // Curly braces which were not present in a template were added into translation.
 | 
					
						
							|  |  |  |  |         [computeMsgId('Text')]: 'Text { count }', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       expect(() => initWithTemplate(AppComp, '<div i18n>Text</div>')) | 
					
						
							|  |  |  |  |           .toThrowError(/Unable to parse ICU expression in "Text { count }" message./); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should handle extra HTML in translation as plain text', () => { | 
					
						
							|  |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       // Translation contains HTML tags that were not present in original message.
 | 
					
						
							|  |  |  |  |       [computeMsgId('Text')]: 'Text <div *ngIf="true">Extra content</div>', | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     const fixture = initWithTemplate(AppComp, '<div i18n>Text</div>'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const element = fixture.nativeElement; | 
					
						
							|  |  |  |  |     expect(element).toHaveText('Text <div *ngIf="true">Extra content</div>'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 11:55:50 -08:00
										 |  |  |  |   it('should reflect lifecycle hook changes in text interpolations in i18n block', () => { | 
					
						
							|  |  |  |  |     @Directive({selector: 'input'}) | 
					
						
							|  |  |  |  |     class InputsDir { | 
					
						
							|  |  |  |  |       constructor(private elementRef: ElementRef) {} | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       ngOnInit() { | 
					
						
							|  |  |  |  |         this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-12-16 11:55:50 -08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         <input #myinput> | 
					
						
							|  |  |  |  |         <div i18n>{{myinput.value}}</div> | 
					
						
							|  |  |  |  |       `
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class App { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [App, InputsDir]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toContain('value set in Directive.ngOnInit'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should reflect lifecycle hook changes in text interpolations in i18n attributes', () => { | 
					
						
							|  |  |  |  |     @Directive({selector: 'input'}) | 
					
						
							|  |  |  |  |     class InputsDir { | 
					
						
							|  |  |  |  |       constructor(private elementRef: ElementRef) {} | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |       ngOnInit() { | 
					
						
							|  |  |  |  |         this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-12-16 11:55:50 -08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         <input #myinput> | 
					
						
							|  |  |  |  |         <div i18n-title title="{{myinput.value}}"></div> | 
					
						
							|  |  |  |  |       `
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class App { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [App, InputsDir]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.querySelector('div').title) | 
					
						
							|  |  |  |  |         .toContain('value set in Directive.ngOnInit'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   it('should not alloc expando slots when there is no new variable to create', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     loadTranslations({ | 
					
						
							|  |  |  |  |       [computeMsgId('{$START_TAG_DIV} Some content {$CLOSE_TAG_DIV}')]: | 
					
						
							|  |  |  |  |           '{$START_TAG_DIV} Some content {$CLOSE_TAG_DIV}', | 
					
						
							|  |  |  |  |       [computeMsgId( | 
					
						
							|  |  |  |  |           '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}')]: | 
					
						
							|  |  |  |  |           '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}', | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							| 
									
										
										
										
											2019-07-08 14:59:10 +02:00
										 |  |  |  |       <div dialog i18n> | 
					
						
							|  |  |  |  |           <div *ngIf="data"> | 
					
						
							|  |  |  |  |               Some content | 
					
						
							|  |  |  |  |           </div> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |       <button [close]="true">Button label</button> | 
					
						
							|  |  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }) | 
					
						
							|  |  |  |  |     class ContentElementDialog { | 
					
						
							|  |  |  |  |       data = false; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-08 14:59:10 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     TestBed.configureTestingModule({declarations: [DialogDir, CloseBtn, ContentElementDialog]}); | 
					
						
							| 
									
										
										
										
											2019-07-08 14:59:10 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     const fixture = TestBed.createComponent(ContentElementDialog); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Remove the reflect attribute, because the attribute order in innerHTML
 | 
					
						
							|  |  |  |  |     // isn't guaranteed in different browsers so it could throw off our assertions.
 | 
					
						
							|  |  |  |  |     const button = fixture.nativeElement.querySelector('button'); | 
					
						
							|  |  |  |  |     button.removeAttribute('ng-reflect-dialog-result'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     expect(fixture.nativeElement.innerHTML).toEqual(`<div dialog=""><!--bindings={
 | 
					
						
							| 
									
										
										
										
											2019-07-08 14:59:10 +02:00
										 |  |  |  |   "ng-reflect-ng-if": "false" | 
					
						
							| 
									
										
										
										
											2019-12-11 22:04:31 +01:00
										 |  |  |  | }--></div><button title="Close dialog">Button label</button>`);
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-02-11 14:44:13 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   describe('ngTemplateOutlet', () => { | 
					
						
							|  |  |  |  |     it('should work with i18n content that includes elements', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}A{$CLOSE_TAG_SPAN} B ')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}a{$CLOSE_TAG_SPAN} b', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <ng-container *ngTemplateOutlet="tmpl"></ng-container> | 
					
						
							|  |  |  |  |         <ng-template #tmpl i18n> | 
					
						
							|  |  |  |  |           <span>A</span> B | 
					
						
							|  |  |  |  |         </ng-template> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toContain('a b'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should work with i18n content that includes other templates (*ngIf)', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_SPAN}A{$CLOSE_TAG_SPAN} B ')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_SPAN}a{$CLOSE_TAG_SPAN} b', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = initWithTemplate(AppComp, `
 | 
					
						
							|  |  |  |  |         <ng-container *ngTemplateOutlet="tmpl"></ng-container> | 
					
						
							|  |  |  |  |         <ng-template #tmpl i18n> | 
					
						
							|  |  |  |  |           <span *ngIf="visible">A</span> B | 
					
						
							|  |  |  |  |         </ng-template> | 
					
						
							|  |  |  |  |       `);
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toContain('a b'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     it('should work with i18n content that includes projection', () => { | 
					
						
							|  |  |  |  |       loadTranslations({ | 
					
						
							|  |  |  |  |         [computeMsgId('{$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT} B ')]: | 
					
						
							|  |  |  |  |             '{$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT} b', | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'projector', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <ng-container *ngTemplateOutlet="tmpl"></ng-container> | 
					
						
							|  |  |  |  |           <ng-template #tmpl i18n> | 
					
						
							|  |  |  |  |             <ng-content></ng-content> B | 
					
						
							|  |  |  |  |           </ng-template> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class Projector { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         selector: 'app', | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |           <projector>a</projector> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class AppComponent { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [AppComponent, Projector]}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(AppComponent); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toContain('a b'); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-09-25 15:01:56 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   describe('viewContainerRef with i18n', () => { | 
					
						
							|  |  |  |  |     it('should create ViewContainerRef with i18n', () => { | 
					
						
							|  |  |  |  |       // This test demonstrates an issue with creating a `ViewContainerRef` and having i18n at the
 | 
					
						
							|  |  |  |  |       // parent element. The reason this broke is that in this case the `ViewContainerRef` creates
 | 
					
						
							|  |  |  |  |       // an dynamic anchor comment but uses `HostTNode` for it which is incorrect. `appendChild`
 | 
					
						
							|  |  |  |  |       // then tries to add internationalization to the comment node and fails.
 | 
					
						
							|  |  |  |  |       @Component({ | 
					
						
							|  |  |  |  |         template: `
 | 
					
						
							|  |  |  |  |             <div i18n>before|<div myDir>inside</div>|after</div> | 
					
						
							|  |  |  |  |           `
 | 
					
						
							|  |  |  |  |       }) | 
					
						
							|  |  |  |  |       class MyApp { | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       @Directive({selector: '[myDir]'}) | 
					
						
							|  |  |  |  |       class MyDir { | 
					
						
							|  |  |  |  |         constructor(vcRef: ViewContainerRef) { | 
					
						
							|  |  |  |  |           myDir = this; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       let myDir!: MyDir; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       TestBed.configureTestingModule({declarations: [MyApp, MyDir]}); | 
					
						
							|  |  |  |  |       const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |       fixture.detectChanges(); | 
					
						
							|  |  |  |  |       expect(myDir).toBeDefined(); | 
					
						
							|  |  |  |  |       expect(fixture.nativeElement.textContent).toEqual(`before|inside|after`); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should create ICU with attributes', () => { | 
					
						
							|  |  |  |  |     // This test demonstrates an issue with setting attributes on ICU elements.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |             <h1 class="num-cart-items" i18n *ngIf="true">{ | 
					
						
							|  |  |  |  |               registerItemCount, plural, | 
					
						
							|  |  |  |  |               =0 {Your cart} | 
					
						
							|  |  |  |  |               =1 {Your cart <span class="item-count">(1 item)</span>} | 
					
						
							|  |  |  |  |               other { | 
					
						
							|  |  |  |  |                 Your cart <span class="item-count">({{ | 
					
						
							|  |  |  |  |                   registerItemCount | 
					
						
							|  |  |  |  |                 }} items)</span> | 
					
						
							|  |  |  |  |               } | 
					
						
							|  |  |  |  |           }</h1>`
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |       registerItemCount = 1; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`Your cart (1 item)`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should not insertBeforeIndex non-projected content text', () => { | 
					
						
							|  |  |  |  |     // This test demonstrates an issue with setting attributes on ICU elements.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({template: `<div i18n>before|<child>TextNotProjected</child>|after</div>`}) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       selector: 'child', | 
					
						
							|  |  |  |  |       template: 'CHILD', | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class Child { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp, Child]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`before|CHILD|after`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should create a pipe inside i18n block', () => { | 
					
						
							|  |  |  |  |     // This test demonstrates an issue with i18n messing up `getCurrentTNode` which subsequently
 | 
					
						
							|  |  |  |  |     // breaks the DI. The issue is that the `i18nStartFirstCreatePass` would create placeholder
 | 
					
						
							|  |  |  |  |     // NODES, and than leave `getCurrentTNode` in undetermined state which would then break DI.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |       <div i18n [title]="null | async"><div>A</div></div> | 
					
						
							|  |  |  |  |       <div i18n>{{(null | async)||'B'}}<div></div></div>`
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`AB`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should copy injector information unto placeholder', () => { | 
					
						
							|  |  |  |  |     // This test demonstrates an issue with i18n Placeholders loosing `injectorIndex` information.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         <parent i18n> | 
					
						
							|  |  |  |  |           <middle> | 
					
						
							|  |  |  |  |             <child>Text</child> | 
					
						
							|  |  |  |  |           </middle> | 
					
						
							|  |  |  |  |         </parent>`
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({selector: 'parent'}) | 
					
						
							|  |  |  |  |     class Parent { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({selector: 'middle'}) | 
					
						
							|  |  |  |  |     class Middle { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     @Component({selector: 'child'}) | 
					
						
							|  |  |  |  |     class Child { | 
					
						
							|  |  |  |  |       constructor(public middle: Middle) { | 
					
						
							|  |  |  |  |         child = this; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     let child!: Child; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp, Parent, Middle, Child]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(child.middle).toBeInstanceOf(Middle); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should allow container in gotClosestRElement', () => { | 
					
						
							|  |  |  |  |     // A second iteration of the loop will have `Container` `TNode`s pass through the system.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |       <div *ngFor="let i of [1,2]"> | 
					
						
							|  |  |  |  |         <ng-template #tmpl i18n><span *ngIf="true">X</span></ng-template> | 
					
						
							|  |  |  |  |         <span [ngTemplateOutlet]="tmpl"></span> | 
					
						
							|  |  |  |  |       </div>`
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`XX`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should link text after ICU', () => { | 
					
						
							|  |  |  |  |     // i18n block must restore the current `currentTNode` so that trailing text node can link to it.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         <ng-container *ngFor="let index of [1, 2]"> | 
					
						
							|  |  |  |  |           {{'['}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {1} other {*}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {one} other {many}} | 
					
						
							|  |  |  |  |           {{'-'}} | 
					
						
							|  |  |  |  |           <span>+</span> | 
					
						
							|  |  |  |  |           {{'-'}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {first} other {rest}} | 
					
						
							|  |  |  |  |           {{']'}} | 
					
						
							|  |  |  |  |         </ng-container> | 
					
						
							|  |  |  |  |         / | 
					
						
							|  |  |  |  |         <ng-container *ngFor="let index of [1, 2]" i18n> | 
					
						
							|  |  |  |  |           {{'['}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {1} other {*}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {one} other {many}} | 
					
						
							|  |  |  |  |           {{'-'}} | 
					
						
							|  |  |  |  |           <span>+</span> | 
					
						
							|  |  |  |  |           {{'-'}} | 
					
						
							|  |  |  |  |           {index, plural, =1 {first} other {rest}} | 
					
						
							|  |  |  |  |           {{']'}} | 
					
						
							|  |  |  |  |         </ng-container> | 
					
						
							|  |  |  |  |       `
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     const textContent = fixture.nativeElement.textContent as string; | 
					
						
							|  |  |  |  |     expect(textContent.split('/').map(s => s.trim())).toEqual([ | 
					
						
							|  |  |  |  |       '[ 1 one - + - first ]  [ * many - + - rest ]', | 
					
						
							|  |  |  |  |       '[ 1 one - + - first ]  [ * many - + - rest ]', | 
					
						
							|  |  |  |  |     ]); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should ignore non-instantiated ICUs on update', () => { | 
					
						
							|  |  |  |  |     // Demonstrates an issue of same selector expression used in nested ICUs, causes non
 | 
					
						
							|  |  |  |  |     // instantiated nested ICUs to be updated.
 | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         before| | 
					
						
							|  |  |  |  |         { retention.unit, select, | 
					
						
							|  |  |  |  |           SECONDS { | 
					
						
							|  |  |  |  |               {retention.durationInUnits, plural, | 
					
						
							|  |  |  |  |                   =1 {1 second} | 
					
						
							|  |  |  |  |                   other {{{retention.durationInUnits}} seconds} | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |               } | 
					
						
							|  |  |  |  |           DAYS { | 
					
						
							|  |  |  |  |               {retention.durationInUnits, plural, | 
					
						
							|  |  |  |  |                   =1 {1 day} | 
					
						
							|  |  |  |  |                   other {{{retention.durationInUnits}} days} | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |               } | 
					
						
							|  |  |  |  |           MONTHS { | 
					
						
							|  |  |  |  |               {retention.durationInUnits, plural, | 
					
						
							|  |  |  |  |                   =1 {1 month} | 
					
						
							|  |  |  |  |                   other {{{retention.durationInUnits}} months} | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |               } | 
					
						
							|  |  |  |  |           YEARS { | 
					
						
							|  |  |  |  |               {retention.durationInUnits, plural, | 
					
						
							|  |  |  |  |                   =1 {1 year} | 
					
						
							|  |  |  |  |                   other {{{retention.durationInUnits}} years} | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |               } | 
					
						
							|  |  |  |  |           other {} | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         |after. | 
					
						
							|  |  |  |  |       `
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |       retention = { | 
					
						
							|  |  |  |  |         durationInUnits: 10, | 
					
						
							|  |  |  |  |         unit: 'SECONDS', | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     const textContent = fixture.nativeElement.textContent as string; | 
					
						
							|  |  |  |  |     expect(textContent.replace(/\s+/g, ' ').trim()).toEqual(`before| 10 seconds |after.`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should render attributes defined in ICUs', () => { | 
					
						
							|  |  |  |  |     // NOTE: This test is extracted from g3.
 | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |         <div i18n>{ | 
					
						
							|  |  |  |  |           parameters.length, | 
					
						
							|  |  |  |  |           plural, | 
					
						
							|  |  |  |  |           =1 {Affects parameter <span class="parameter-name" attr="should_be_present">{{parameters[0].name}}</span>} | 
					
						
							|  |  |  |  |           other {Affects {{parameters.length}} parameters, including <span | 
					
						
							|  |  |  |  |               class="parameter-name">{{parameters[0].name}}</span>} | 
					
						
							|  |  |  |  |           }</div> | 
					
						
							|  |  |  |  |         `
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |       parameters = [{name: 'void_abt_param'}]; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     const span = (fixture.nativeElement as HTMLElement).querySelector('span')!; | 
					
						
							|  |  |  |  |     expect(span.getAttribute('attr')).toEqual('should_be_present'); | 
					
						
							|  |  |  |  |     expect(span.getAttribute('class')).toEqual('parameter-name'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support different ICUs cases for each *ngFor iteration', () => { | 
					
						
							|  |  |  |  |     @Component({ | 
					
						
							|  |  |  |  |       template: `
 | 
					
						
							|  |  |  |  |       <ul i18n> | 
					
						
							|  |  |  |  |         <li *ngFor="let item of items">{ | 
					
						
							|  |  |  |  |           item, plural, | 
					
						
							|  |  |  |  |           =1 {<b>one</b>} | 
					
						
							|  |  |  |  |           =2 {<i>two</i>} | 
					
						
							|  |  |  |  |       },</li> | 
					
						
							|  |  |  |  |       </ul>`
 | 
					
						
							|  |  |  |  |     }) | 
					
						
							|  |  |  |  |     class MyApp { | 
					
						
							|  |  |  |  |       items = [1, 2]; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [MyApp]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(MyApp); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`one,two,`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     fixture.componentInstance.items = [2, 1]; | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.textContent).toEqual(`two,one,`); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-10-24 13:16:11 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should be able to inject a static i18n attribute', () => { | 
					
						
							|  |  |  |  |     loadTranslations({[computeMsgId('text')]: 'translatedText'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Directive({selector: '[injectTitle]'}) | 
					
						
							|  |  |  |  |     class InjectTitleDir { | 
					
						
							|  |  |  |  |       constructor(@Attribute('title') public title: string) {} | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({template: `<div i18n-title title="text" injectTitle></div>`}) | 
					
						
							|  |  |  |  |     class App { | 
					
						
							|  |  |  |  |       @ViewChild(InjectTitleDir) dir!: InjectTitleDir; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [App, InjectTitleDir]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.componentInstance.dir.title).toBe('translatedText'); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.querySelector('div').getAttribute('title')).toBe('translatedText'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should inject `null` for an i18n attribute with an interpolation', () => { | 
					
						
							|  |  |  |  |     loadTranslations({[computeMsgId('text {$INTERPOLATION}')]: 'translatedText {$INTERPOLATION}'}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Directive({selector: '[injectTitle]'}) | 
					
						
							|  |  |  |  |     class InjectTitleDir { | 
					
						
							|  |  |  |  |       constructor(@Attribute('title') public title: string) {} | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @Component({template: `<div i18n-title title="text {{ value }}" injectTitle></div>`}) | 
					
						
							|  |  |  |  |     class App { | 
					
						
							|  |  |  |  |       @ViewChild(InjectTitleDir) dir!: InjectTitleDir; | 
					
						
							|  |  |  |  |       value = 'value'; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     TestBed.configureTestingModule({declarations: [App, InjectTitleDir]}); | 
					
						
							|  |  |  |  |     const fixture = TestBed.createComponent(App); | 
					
						
							|  |  |  |  |     fixture.detectChanges(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(fixture.componentInstance.dir.title).toBeNull(); | 
					
						
							|  |  |  |  |     expect(fixture.nativeElement.querySelector('div').getAttribute('title')) | 
					
						
							|  |  |  |  |         .toBe('translatedText value'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | function initWithTemplate(compType: Type<any>, template: string) { | 
					
						
							|  |  |  |  |   TestBed.overrideComponent(compType, {set: {template}}); | 
					
						
							|  |  |  |  |   const fixture = TestBed.createComponent(compType); | 
					
						
							|  |  |  |  |   fixture.detectChanges(); | 
					
						
							|  |  |  |  |   return fixture; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @Component({selector: 'app-comp', template: ``}) | 
					
						
							|  |  |  |  | class AppComp { | 
					
						
							|  |  |  |  |   name = `Angular`; | 
					
						
							|  |  |  |  |   visible = true; | 
					
						
							|  |  |  |  |   count = 0; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-09 17:21:31 -07:00
										 |  |  |  | @Component({ | 
					
						
							|  |  |  |  |   selector: 'app-comp-with-whitespaces', | 
					
						
							|  |  |  |  |   template: ``, | 
					
						
							|  |  |  |  |   preserveWhitespaces: true, | 
					
						
							|  |  |  |  | }) | 
					
						
							|  |  |  |  | class AppCompWithWhitespaces { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | @Directive({ | 
					
						
							|  |  |  |  |   selector: '[tplRef]', | 
					
						
							|  |  |  |  | }) | 
					
						
							|  |  |  |  | class DirectiveWithTplRef { | 
					
						
							|  |  |  |  |   constructor(public vcRef: ViewContainerRef, public tplRef: TemplateRef<{}>) {} | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |   ngOnInit() { | 
					
						
							|  |  |  |  |     this.vcRef.createEmbeddedView(this.tplRef, {}); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-06 15:33:34 +02:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-07 16:35:58 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | @Pipe({name: 'uppercase'}) | 
					
						
							|  |  |  |  | class UppercasePipe implements PipeTransform { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |  |   transform(value: string) { | 
					
						
							|  |  |  |  |     return value.toUpperCase(); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-07-07 16:35:58 +02:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-08 14:59:10 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | @Directive({selector: `[dialog]`}) | 
					
						
							|  |  |  |  | export class DialogDir { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | @Directive({selector: `button[close]`, host: {'[title]': 'name'}}) | 
					
						
							|  |  |  |  | export class CloseBtn { | 
					
						
							|  |  |  |  |   @Input('close') dialogResult: any; | 
					
						
							|  |  |  |  |   name: string = 'Close dialog'; | 
					
						
							|  |  |  |  | } |