import {describe, expect, it, iit, ddescribe} from "@angular/core/testing/testing_internal"; import {I18nHtmlParser} from "@angular/compiler/src/i18n/i18n_html_parser"; import {Message, id} from "@angular/compiler/src/i18n/message"; import {Parser} from "@angular/compiler/src/expression_parser/parser"; import {Lexer} from "@angular/compiler/src/expression_parser/lexer"; import {StringMapWrapper} from "../../src/facade/collection"; import {HtmlParser, HtmlParseTreeResult} from "@angular/compiler/src/html_parser"; import {HtmlElementAst, HtmlAttrAst, HtmlTextAst} from "@angular/compiler/src/html_ast"; import {deserializeXmb} from "@angular/compiler/src/i18n/xmb_serializer"; import {ParseError} from "@angular/compiler/src/parse_util"; import {humanizeDom} from "@angular/compiler/test/html_ast_spec_utils"; export function main() { describe('I18nHtmlParser', () => { function parse(template: string, messages: {[key: string]: string}, implicitTags: string[] = [], implicitAttrs: {[k: string]: string[]} = {}): HtmlParseTreeResult { var parser = new Parser(new Lexer()); let htmlParser = new HtmlParser(); let msgs = ''; StringMapWrapper.forEach(messages, (v, k) => msgs += `${v}`); let res = deserializeXmb(`${msgs}`, 'someUrl'); return new I18nHtmlParser(htmlParser, parser, res.content, res.messages, implicitTags, implicitAttrs) .parse(template, "someurl", true); } it("should delegate to the provided parser when no i18n", () => { expect(humanizeDom(parse('
a
', {}))) .toEqual([[HtmlElementAst, 'div', 0], [HtmlTextAst, 'a', 1]]); }); it("should replace attributes", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("some message", "meaning", null))] = "another message"; expect(humanizeDom(parse("
", translations))) .toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', 'another message']]); }); it("should replace elements with the i18n attr", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("message", "meaning", null))] = "another message"; expect(humanizeDom(parse("
message
", translations))) .toEqual([[HtmlElementAst, 'div', 0], [HtmlTextAst, 'another message', 1]]); }); it("should handle interpolation", () => { let translations: {[key: string]: string} = {}; translations[id(new Message(' and ', null, null))] = ' or '; expect(humanizeDom(parse("
", translations))) .toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{{b}} or {{a}}']]); }); it('should handle interpolation with custom placeholder names', () => { let translations: {[key: string]: string} = {}; translations[id(new Message(' and ', null, null))] = ' or '; expect( humanizeDom(parse( `
`, translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{{b //i18n(ph="SECOND")}} or {{a //i18n(ph="FIRST")}}'] ]); }); it('should handle interpolation with duplicate placeholder names', () => { let translations: {[key: string]: string} = {}; translations[id(new Message(' and ', null, null))] = ' or '; expect( humanizeDom(parse( `
`, translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{{b //i18n(ph="FIRST")}} or {{a //i18n(ph="FIRST")}}'] ]); }); it("should handle nested html", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('ab', null, null))] = 'BA'; expect(humanizeDom(parse('
ab
', translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlElementAst, 'b', 1], [HtmlTextAst, 'B', 2], [HtmlElementAst, 'a', 1], [HtmlTextAst, 'A', 2], ]); }); it("should support interpolation", () => { let translations: {[key: string]: string} = {}; translations[id(new Message( 'ab', null, null))] = 'BA'; expect(humanizeDom(parse('
ab{{i}}
', translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlElementAst, 'b', 1], [HtmlTextAst, '{{i}}B', 2], [HtmlElementAst, 'a', 1], [HtmlTextAst, 'A', 2], ]); }); it("should i18n attributes of placeholder elements", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('a', null, null))] = 'A'; translations[id(new Message('b', null, null))] = 'B'; expect(humanizeDom(parse('', translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlElementAst, 'a', 1], [HtmlAttrAst, 'value', "B"], [HtmlTextAst, 'A', 2], ]); }); it("should preserve non-i18n attributes", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('message', null, null))] = 'another message'; expect(humanizeDom(parse('
message
', translations))) .toEqual([ [HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', "b"], [HtmlTextAst, 'another message', 1] ]); }); it('should extract from partitions', () => { let translations: {[key: string]: string} = {}; translations[id(new Message('message1', 'meaning1', null))] = 'another message1'; translations[id(new Message('message2', 'meaning2', null))] = 'another message2'; let res = parse(`message1message2`, translations); expect(humanizeDom(res)) .toEqual([ [HtmlTextAst, 'another message1', 0], [HtmlTextAst, 'another message2', 0], ]); }); it("should preserve original positions", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('ab', null, null))] = 'BA'; let res = (parse('
ab
', translations).rootNodes[0]).children; expect(res[0].sourceSpan.start.offset).toEqual(18); expect(res[1].sourceSpan.start.offset).toEqual(10); }); it("should handle the plural expansion form", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('zerobold', "plural_0", null))] = 'ZEROBOLD'; let res = parse(`{messages.length, plural,=0 {zerobold}}`, translations); expect(humanizeDom(res)) .toEqual([ [HtmlElementAst, 'ul', 0], [HtmlAttrAst, '[ngPlural]', 'messages.length'], [HtmlElementAst, 'template', 1], [HtmlAttrAst, 'ngPluralCase', '0'], [HtmlElementAst, 'li', 2], [HtmlTextAst, 'ZERO', 3], [HtmlElementAst, 'b', 3], [HtmlTextAst, 'BOLD', 4] ]); }); it("should handle nested expansion forms", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('m', "gender_m", null))] = 'M'; let res = parse(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, translations); expect(humanizeDom(res)) .toEqual([ [HtmlElementAst, 'ul', 0], [HtmlAttrAst, '[ngPlural]', 'messages.length'], [HtmlElementAst, 'template', 1], [HtmlAttrAst, 'ngPluralCase', '0'], [HtmlElementAst, 'li', 2], [HtmlElementAst, 'ul', 3], [HtmlAttrAst, '[ngSwitch]', 'p.gender'], [HtmlElementAst, 'template', 4], [HtmlAttrAst, 'ngSwitchWhen', 'm'], [HtmlElementAst, 'li', 5], [HtmlTextAst, 'M', 6], [HtmlTextAst, ' ', 3] ]); }); it("should correctly set source code positions", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('bold', "plural_0", null))] = 'BOLD'; let nodes = parse(`{messages.length, plural,=0 {bold}}`, translations).rootNodes; let ul: HtmlElementAst = nodes[0]; expect(ul.sourceSpan.start.col).toEqual(0); expect(ul.sourceSpan.end.col).toEqual(42); expect(ul.startSourceSpan.start.col).toEqual(0); expect(ul.startSourceSpan.end.col).toEqual(42); expect(ul.endSourceSpan.start.col).toEqual(0); expect(ul.endSourceSpan.end.col).toEqual(42); let switchExp = ul.attrs[0]; expect(switchExp.sourceSpan.start.col).toEqual(1); expect(switchExp.sourceSpan.end.col).toEqual(16); let template: HtmlElementAst = ul.children[0]; expect(template.sourceSpan.start.col).toEqual(26); expect(template.sourceSpan.end.col).toEqual(41); let switchCheck = template.attrs[0]; expect(switchCheck.sourceSpan.start.col).toEqual(26); expect(switchCheck.sourceSpan.end.col).toEqual(28); let li: HtmlElementAst = template.children[0]; expect(li.sourceSpan.start.col).toEqual(26); expect(li.sourceSpan.end.col).toEqual(41); let b: HtmlElementAst = li.children[0]; expect(b.sourceSpan.start.col).toEqual(29); expect(b.sourceSpan.end.col).toEqual(32); }); it("should handle other special forms", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('m', "gender_male", null))] = 'M'; let res = parse(`{person.gender, gender,=male {m}}`, translations); expect(humanizeDom(res)) .toEqual([ [HtmlElementAst, 'ul', 0], [HtmlAttrAst, '[ngSwitch]', 'person.gender'], [HtmlElementAst, 'template', 1], [HtmlAttrAst, 'ngSwitchWhen', 'male'], [HtmlElementAst, 'li', 2], [HtmlTextAst, 'M', 3], ]); }); describe("errors", () => { it("should error when giving an invalid template", () => { expect(humanizeErrors(parse("a", {}).errors)) .toEqual(['Unexpected closing tag "b"']); }); it("should error when no matching message (attr)", () => { let mid = id(new Message("some message", null, null)); expect(humanizeErrors(parse("
", {}).errors)) .toEqual([`Cannot find message for id '${mid}', content 'some message'.`]); }); it("should error when no matching message (text)", () => { let mid = id(new Message("some message", null, null)); expect(humanizeErrors(parse("
some message
", {}).errors)) .toEqual([`Cannot find message for id '${mid}', content 'some message'.`]); }); it("should error when a non-placeholder element appears in translation", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("some message", null, null))] = "
a"; expect(humanizeErrors(parse("
some message
", translations).errors)) .toEqual([`Unexpected tag "a". Only "ph" tags are allowed.`]); }); it("should error when a placeholder element does not have the name attribute", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("some message", null, null))] = "a"; expect(humanizeErrors(parse("
some message
", translations).errors)) .toEqual([`Missing "name" attribute.`]); }); it("should error when the translation refers to an invalid expression", () => { let translations: {[key: string]: string} = {}; translations[id(new Message('hi ', null, null))] = 'hi '; expect( humanizeErrors(parse("
", translations).errors)) .toEqual(["Invalid interpolation name '99'"]); }); describe('implicit translation', () => { it("should support attributes", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("some message", null, null))] = "another message"; expect(humanizeDom(parse("", translations, [], {'i18n-el': ['value']}))) .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]); }); it("should support attributes with meaning and description", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("some message", "meaning", "description"))] = "another message"; expect(humanizeDom(parse( "", translations, [], {'i18n-el': ['value']}))) .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]); }); it("should support elements", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("message", null, null))] = "another message"; expect(humanizeDom(parse("message", translations, ['i18n-el']))) .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]); }); it("should support elements with meaning and description", () => { let translations: {[key: string]: string} = {}; translations[id(new Message("message", "meaning", "description"))] = "another message"; expect(humanizeDom(parse("message", translations, ['i18n-el']))) .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]); }); }); }); }); } function humanizeErrors(errors: ParseError[]): string[] { return errors.map(error => error.msg); }