From 56c361ff6adee8c9d03e495f04eaab8a9d4cc1f4 Mon Sep 17 00:00:00 2001 From: maxime-allex Date: Tue, 6 Dec 2016 15:04:59 +0100 Subject: [PATCH] test(compiler): test i18n explicit id closes #13272 --- .../@angular/compiler/src/i18n/i18n_parser.ts | 3 +- .../compiler/test/i18n/digest_spec.ts | 15 +- .../test/i18n/extractor_merger_spec.ts | 135 ++++++++++++------ .../compiler/test/i18n/integration_spec.ts | 12 +- .../test/i18n/serializers/xliff_spec.ts | 24 ++++ .../test/i18n/serializers/xmb_spec.ts | 6 + .../test/i18n/translation_bundle_spec.ts | 18 +-- 7 files changed, 154 insertions(+), 59 deletions(-) diff --git a/modules/@angular/compiler/src/i18n/i18n_parser.ts b/modules/@angular/compiler/src/i18n/i18n_parser.ts index e3af025a50..cac8435660 100644 --- a/modules/@angular/compiler/src/i18n/i18n_parser.ts +++ b/modules/@angular/compiler/src/i18n/i18n_parser.ts @@ -40,7 +40,8 @@ class _I18nVisitor implements html.Visitor { private _expressionParser: ExpressionParser, private _interpolationConfig: InterpolationConfig) {} - public toI18nMessage(nodes: html.Node[], meaning: string, description: string, id: string): i18n.Message { + public toI18nMessage(nodes: html.Node[], meaning: string, description: string, id: string): + i18n.Message { this._isIcu = nodes.length == 1 && nodes[0] instanceof html.Expansion; this._icuDepth = 0; this._placeholderRegistry = new PlaceholderRegistry(); diff --git a/modules/@angular/compiler/test/i18n/digest_spec.ts b/modules/@angular/compiler/test/i18n/digest_spec.ts index 07b7bbe390..7297f6cc76 100644 --- a/modules/@angular/compiler/test/i18n/digest_spec.ts +++ b/modules/@angular/compiler/test/i18n/digest_spec.ts @@ -6,10 +6,23 @@ * found in the LICENSE file at https://angular.io/license */ -import {computeMsgId, sha1} from '../../src/i18n/digest'; +import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; export function main(): void { describe('digest', () => { + describe('digest', () => { + it('must return the ID if it\'s explicit', () => { + expect(digest({ + id: 'i', + nodes: [], + placeholders: {}, + placeholderToMessage: {}, + meaning: '', + description: '', + })).toEqual('i'); + }); + }); + describe('sha1', () => { it('should work on empty strings', () => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); }); diff --git a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts index c175425507..0e88c7359d 100644 --- a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts +++ b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts @@ -20,7 +20,10 @@ export function main() { describe('elements', () => { it('should extract from elements', () => { expect(extract('
textnested
')).toEqual([ - [['text', 'nested'], 'm', 'd|e'], + [ + ['text', 'nested'], 'm', 'd|e', + '' + ], ]); }); @@ -29,11 +32,45 @@ export function main() { extract( '
nested
')) .toEqual([ - [['nested'], 'm1', 'd1'], - [['single child'], 'm2', 'd2'], + [['nested'], 'm1', 'd1', ''], + [['single child'], 'm2', 'd2', ''], ]); }); + it('should extract from attributes with id', () => { + expect( + extract( + '
nested
')) + .toEqual([ + [ + ['nested'], 'm1', 'd1', + 'i1' + ], + [['single child'], 'm2', 'd2', 'i2'], + ]); + }); + + it('should extract from attributes without meaning and with id', () => { + expect( + extract( + '
nested
')) + .toEqual([ + [['nested'], '', 'd1', 'i1'], + [['single child'], '', 'd2', 'i2'], + ]); + }); + + it('should extract from attributes with id only', () => { + expect( + extract( + '
nested
')) + .toEqual([ + [['nested'], '', '', 'i1'], + [['single child'], '', '', 'i2'], + ]); + }); + + it('should extract from ICU messages', () => { expect( extract( @@ -43,10 +80,10 @@ export function main() { [ '{count, plural, =0 {[]}}' ], - 'm', 'd' + 'm', 'd', '' ], - [['title'], '', ''], - [['desc'], '', ''], + [['title'], '', '', ''], + [['desc'], '', '', ''], ]); }); @@ -55,7 +92,7 @@ export function main() { it('should ignore implicit elements in translatable elements', () => { expect(extract('

', ['p'])).toEqual([ - [[''], 'm', 'd'] + [[''], 'm', 'd', ''] ]); }); }); @@ -64,17 +101,19 @@ export function main() { it('should extract from blocks', () => { expect(extract(`message1 message2 - message3`)) + message3 + message4 + message5`)) .toEqual([ - [['message1'], 'meaning1', 'desc1'], - [['message2'], '', 'desc2'], - [['message3'], '', ''], + [['message1'], 'meaning1', 'desc1', ''], [['message2'], '', 'desc2', ''], + [['message3'], '', '', ''], [['message4'], 'meaning4', 'desc4', 'id4'], + [['message5'], '', '', 'id5'] ]); }); it('should ignore implicit elements in blocks', () => { expect(extract('

', ['p'])).toEqual([ - [[''], 'm', 'd'] + [[''], 'm', 'd', ''] ]); }); @@ -88,7 +127,7 @@ export function main() { [ '{count, plural, =0 {[html]}}' ], - '', '' + '', '', '' ], [ [ @@ -98,15 +137,15 @@ export function main() { ' name="START_TAG_SPAN">html]}}', '[interp]' ], - '', '' + '', '', '' ], ]); }); it('should ignore other comments', () => { - expect(extract(`message1`)) + expect(extract(`message1`)) .toEqual([ - [['message1'], 'meaning1', 'desc1'], + [['message1'], 'meaning1', 'desc1', 'id1'], ]); }); @@ -118,34 +157,37 @@ export function main() { it('should extract ICU messages from translatable elements', () => { // single message when ICU is the only children expect(extract('
{count, plural, =0 {text}}
')).toEqual([ - [['{count, plural, =0 {[text]}}'], 'm', 'd'], + [['{count, plural, =0 {[text]}}'], 'm', 'd', ''], ]); // single message when ICU is the only (implicit) children expect(extract('
{count, plural, =0 {text}}
', ['div'])).toEqual([ - [['{count, plural, =0 {[text]}}'], '', ''], + [['{count, plural, =0 {[text]}}'], '', '', ''], ]); // one message for the element content and one message for the ICU - expect(extract('
before{count, plural, =0 {text}}after
')).toEqual([ - [['before', '{count, plural, =0 {[text]}}', 'after'], 'm', 'd'], - [['{count, plural, =0 {[text]}}'], '', ''], + expect(extract('
before{count, plural, =0 {text}}after
')).toEqual([ + [ + ['before', '{count, plural, =0 {[text]}}', 'after'], 'm', 'd', + 'i' + ], + [['{count, plural, =0 {[text]}}'], '', '', ''], ]); }); it('should extract ICU messages from translatable block', () => { // single message when ICU is the only children expect(extract('{count, plural, =0 {text}}')).toEqual([ - [['{count, plural, =0 {[text]}}'], 'm', 'd'], + [['{count, plural, =0 {[text]}}'], 'm', 'd', ''], ]); // one message for the block content and one message for the ICU expect(extract('before{count, plural, =0 {text}}after')) .toEqual([ - [['{count, plural, =0 {[text]}}'], '', ''], + [['{count, plural, =0 {[text]}}'], '', '', ''], [ ['before', '{count, plural, =0 {[text]}}', 'after'], 'm', - 'd' + 'd', '' ], ]); }); @@ -156,20 +198,20 @@ export function main() { it('should ignore nested ICU messages', () => { expect(extract('
{count, plural, =0 { {sex, select, male {m}} }}
')) .toEqual([ - [['{count, plural, =0 {[{sex, select, male {[m]}}, ]}}'], 'm', 'd'], + [['{count, plural, =0 {[{sex, select, male {[m]}}, ]}}'], 'm', 'd', ''], ]); }); it('should ignore implicit elements in non translatable ICU messages', () => { - expect( - extract( - '
{count, plural, =0 { {sex, select, male {

ignore

}} }}
', - ['p'])) + expect(extract( + '
{count, plural, =0 { {sex, select, male {

ignore

}}' + + ' }}
', + ['p'])) .toEqual([[ [ '{count, plural, =0 {[{sex, select, male {[ignore]}}, ]}}' ], - 'm', 'd' + 'm', 'd', 'i' ]]); }); @@ -181,46 +223,45 @@ export function main() { describe('attributes', () => { it('should extract from attributes outside of translatable sections', () => { - expect(extract('
')).toEqual([ - [['msg'], 'm', 'd'], + expect(extract('
')).toEqual([ + [['msg'], 'm', 'd', 'i'], ]); }); it('should extract from attributes in translatable elements', () => { - expect(extract('

')).toEqual([ + expect(extract('

')).toEqual([ [ [''], - '', '' + '', '', '' ], - [['msg'], 'm', 'd'], + [['msg'], 'm', 'd', 'i'], ]); }); it('should extract from attributes in translatable blocks', () => { expect(extract('

')) .toEqual([ - [['msg'], 'm', 'd'], + [['msg'], 'm', 'd', ''], [ [''], - '', '' + '', '', '' ], ]); }); it('should extract from attributes in translatable ICUs', () => { - expect( - extract( - '{count, plural, =0 {

}}')) + expect(extract(`{count, plural, =0 {

}}`)) .toEqual([ - [['msg'], 'm', 'd'], + [['msg'], 'm', 'd', 'i'], [ [ '{count, plural, =0 {[]}}' ], - '', '' + '', '', '' ], ]); }); @@ -228,7 +269,7 @@ export function main() { it('should extract from attributes in non translatable ICUs', () => { expect(extract('{count, plural, =0 {

}}')) .toEqual([ - [['msg'], 'm', 'd'], + [['msg'], 'm', 'd', ''], ]); }); @@ -239,7 +280,7 @@ export function main() { describe('implicit elements', () => { it('should extract from implicit elements', () => { expect(extract('bolditalic', ['b'])).toEqual([ - [['bold'], '', ''], + [['bold'], '', '', ''], ]); }); @@ -251,7 +292,7 @@ export function main() { }).not.toThrow(); expect(result).toEqual([ - [['outer', 'inner'], '', ''], + [['outer', 'inner'], '', '', ''], ]); }); @@ -261,7 +302,7 @@ export function main() { it('should extract implicit attributes', () => { expect(extract('bolditalic', [], {'b': ['title']})) .toEqual([ - [['bb'], '', ''], + [['bb'], '', '', ''], ]); }); }); @@ -433,7 +474,7 @@ function extract( // clang-format off // https://github.com/angular/clang-format/issues/35 return result.messages.map( - message => [serializeI18nNodes(message.nodes), message.meaning, message.description, ]) as [string[], string, string][]; + message => [serializeI18nNodes(message.nodes), message.meaning, message.description, message.id]) as [string[], string, string][]; // clang-format on } diff --git a/modules/@angular/compiler/test/i18n/integration_spec.ts b/modules/@angular/compiler/test/i18n/integration_spec.ts index ade32405dd..7823c42634 100644 --- a/modules/@angular/compiler/test/i18n/integration_spec.ts +++ b/modules/@angular/compiler/test/i18n/integration_spec.ts @@ -59,14 +59,17 @@ export function main() { tb.detectChanges(); expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('un'); expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('un'); + expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('un'); cmp.count = 2; tb.detectChanges(); expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('deux'); expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('deux'); + expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('deux'); cmp.count = 3; tb.detectChanges(); expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('beaucoup'); expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup'); + expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('beaucoup'); cmp.sex = 'm'; cmp.sexB = 'f'; @@ -90,8 +93,8 @@ export function main() { .toEqual('

Balises dans les commentaires html

'); expectHtml(el, '#i18n-13') .toBe('
'); - expectHtml(el, '#i18n-15').toMatch(/ca devrait<\/b> marcher/); + expectHtml(el, '#i18n-16').toMatch(/avec un ID explicite/); }); }); } @@ -141,6 +144,8 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
it should work
+
with an explicit ID
+
{count, plural, =0 {zero} =1 {one} =2 {two} other {many}}
` }) class I18nComponent { @@ -182,6 +187,9 @@ const XTB = ` ca devrait marcher + avec un ID explicite + {VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<b>beaucoup</b>} } `; // unused, for reference only @@ -210,5 +218,7 @@ const XMB = ` <div></div> it <b>should</b> work + with an explicit ID + {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>} } `; diff --git a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts index 5c8f9af5a3..6d7b3fb7cf 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts @@ -18,6 +18,8 @@ const HTML = `

not translatable

translatable element with placeholders {{ interpolation}}

foo

+

foo

+

foo


`; @@ -39,6 +41,16 @@ const WRITE_XLIFF = ` d m + + foo + + d + m + + + foo + + @@ -67,6 +79,16 @@ const LOAD_XLIFF = ` d m + + foo + toto + d + m + + + foo + tata + @@ -107,6 +129,8 @@ export function main(): void { 'ec1d033f2436133c14ab038286c4f5df4697484a': ' footnemele elbatalsnart sredlohecalp htiw', 'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof', + 'i': 'toto', + 'bar': 'tata', 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': '', }); diff --git a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts index 319370102e..ecfe0b1519 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts @@ -18,6 +18,9 @@ export function main(): void {

translatable element with placeholders {{ interpolation}}

{ count, plural, =0 {

test

}}

foo

+

foo

+

foo

+

{ count, plural, =0 { { sex, select, other {

deeply nested

}} }}

{ count, plural, =0 { { sex, select, other {

deeply nested

}} }}

`; const XMB = ` @@ -46,6 +49,9 @@ export function main(): void { translatable element <b>with placeholders</b> {VAR_PLURAL, plural, =0 {<p>test</p>} } foo + foo + foo + {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<p>deeply nested</p>} } } } {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<p>deeply nested</p>} } } } `; diff --git a/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts b/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts index d6f51e331d..2aa45331f8 100644 --- a/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts +++ b/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts @@ -21,7 +21,7 @@ export function main(): void { it('should translate a plain message', () => { const msgMap = {foo: [new i18n.Text('bar', null)]}; const tb = new TranslationBundle(msgMap, (_) => 'foo'); - const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd'); + const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(serializeNodes(tb.get(msg))).toEqual(['bar']); }); @@ -36,7 +36,7 @@ export function main(): void { ph1: '*phContent*', }; const tb = new TranslationBundle(msgMap, (_) => 'foo'); - const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd'); + const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); expect(serializeNodes(tb.get(msg))).toEqual(['bar*phContent*']); }); @@ -51,8 +51,8 @@ export function main(): void { new i18n.Text('*refMsg*', null), ], }; - const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd'); - const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd'); + const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); + const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i'); let count = 0; const digest = (_: any) => count++ ? 'ref' : 'foo'; const tb = new TranslationBundle(msgMap, digest); @@ -69,13 +69,13 @@ export function main(): void { ] }; const tb = new TranslationBundle(msgMap, (_) => 'foo'); - const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd'); + const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).toThrowError(/Unknown placeholder/); }); it('should report missing translation', () => { const tb = new TranslationBundle({}, (_) => 'foo'); - const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd'); + const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).toThrowError(/Missing translation for message foo/); }); @@ -83,8 +83,8 @@ export function main(): void { const msgMap = { foo: [new i18n.Placeholder('', 'ph1', span)], }; - const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd'); - const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd'); + const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); + const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i'); let count = 0; const digest = (_: any) => count++ ? 'ref' : 'foo'; const tb = new TranslationBundle(msgMap, digest); @@ -102,7 +102,7 @@ export function main(): void { ph1: '
', }; const tb = new TranslationBundle(msgMap, (_) => 'foo'); - const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd'); + const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).toThrowError(/Unexpected closing tag "b"/); }); });