import {
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xdescribe,
xit
} 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 {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
HtmlCommentAst,
htmlVisitAll
} from '@angular/compiler/src/html_ast';
import {serializeXmb, deserializeXmb} from '@angular/compiler/src/i18n/xmb_serializer';
import {ParseError, ParseLocation} 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}): 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)
.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('', 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('', 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('', 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'"]);
});
});
});
}
function humanizeErrors(errors: ParseError[]): string[] {
return errors.map(error => error.msg);
}