var expect = require('expect.js');
var fs = require('fs');
var peg = require('pegjs');
var parse = null;
describe("Parser generation", function() {
it("should generate a valid parser from pegjs source", function() {
var src = fs.readFileSync('./parser.pegjs', 'utf8');
expect(src).to.not.be.empty();
var parser = peg.generate(src);
expect(parser.parse).to.be.a('function');
parse = parser.parse;
});
});
describe("Replacement", function() {
it("should accept string only input", function() {
expect(parse('This is a string')[0]).to.be('This is a string');
expect(parse('☺☺☺☺')[0]).to.be('☺☺☺☺');
expect(parse('This is \n a string')[0]).to.be('This is \n a string');
expect(parse('中国话不用彁字。')[0]).to.be('中国话不用彁字。');
expect(parse(' \t leading whitspace')[0]).to.be(' \t leading whitspace');
expect(parse('trailing whitespace \n ')[0]).to.be('trailing whitespace \n ');
});
it("should allow you to escape { and } characters", function() {
expect(parse("'{'test")[0]).to.eql('{test');
expect(parse("test'}'")[0]).to.eql('test}');
expect(parse("'{test}'")[0]).to.eql('{test}');
});
it("should gracefully handle quotes (since it ends up in a JS String)", function() {
expect(parse('This is a dbl quote: "')[0]).to.eql('This is a dbl quote: "');
expect(parse("This is a single quote: '")[0]).to.eql("This is a single quote: '");
});
it("should accept only a variable", function() {
expect(parse('{test}')).to.be.an('object');
expect(parse('{0}')).to.be.an('object');
});
it("should not care about white space in a variable", function() {
var targetStr = JSON.stringify(parse('{test}'));
expect(JSON.stringify(parse('{test }'))).to.eql(targetStr);
expect(JSON.stringify(parse('{ test}'))).to.eql(targetStr);
expect(JSON.stringify(parse('{test }'))).to.eql(targetStr);
expect(JSON.stringify(parse('{ test}'))).to.eql(targetStr);
expect(JSON.stringify(parse('{test}'))).to.eql(targetStr);
});
it("should maintain exact strings - not affected by variables", function() {
expect(parse('x{test}')[0]).to.be('x');
expect(parse('\n{test}')[0]).to.be('\n');
expect(parse(' {test}')[0]).to.be(' ');
expect(parse('x { test}')[0]).to.be('x ');
expect(parse('x{test} x ')[2]).to.be(' x ');
expect(parse('x\n{test}\n')[0]).to.be('x\n');
expect(parse('x\n{test}\n')[2]).to.be('\n');
});
it("should handle extended character literals", function() {
expect(parse('☺{test}')[0]).to.be('☺');
expect(parse('中{test}中国话不用彁字。')[2]).to.be('中国话不用彁字。');
});
it("shouldn't matter if it has html or something in it", function() {
expect(parse('
content: {TEST}
')[0]).to.be('content: ');
expect(parse('
content: {TEST}
')[1].arg).to.be('TEST');
expect(parse('
content: {TEST}
')[2]).to.be('
');
});
it("should allow you to use extension keywords for plural formats everywhere except where they go", function() {
expect(parse('select select, ')[0]).to.eql('select select, ');
expect(parse('select offset, offset:1 ')[0]).to.eql('select offset, offset:1 ');
expect(parse('one other, =1 ')[0]).to.eql('one other, =1 ');
expect(parse('one {select} ')[1].arg).to.eql('select');
expect(parse('one {plural} ')[1].arg).to.eql('plural');
});
it("should correctly handle apostrophes", function() {
// This mirrors the default DOUBLE_OPTIONAL behavior of ICU.
expect(parse("I see '{many}'")[0]).to.eql("I see {many}");
expect(parse("I said '{''Wow!''}'")[0]).to.eql("I said {'Wow!'}");
expect(parse("I don't know")[0]).to.eql("I don't know");
expect(parse("I don''t know")[0]).to.eql("I don't know");
expect(parse("A'a''a'A")[0]).to.eql("A'a'a'A");
expect(parse("A'{a''a}'A")[0]).to.eql("A{a'a}A");
// # and | are not special here.
expect(parse("A '#' A")[0]).to.eql("A '#' A");
expect(parse("A '|' A")[0]).to.eql("A '|' A");
});
});
describe("Simple arguments", function() {
it("should accept a statement based on a variable", function() {
expect(function(){ parse('{VAR}'); }).to.not.throwError();
});
it("should be very whitespace agnostic", function(){
var res = JSON.stringify(parse('{VAR}'));
expect(JSON.stringify(parse('{VAR}'))).to.eql(res);
expect(JSON.stringify(parse('{ VAR }'))).to.eql(res);
expect(JSON.stringify(parse('{ \n VAR \n}'))).to.eql(res);
expect(JSON.stringify(parse('{ \t VAR \n }'))).to.eql(res);
});
it("should be correctly parsed", function() {
expect(parse('{VAR}')[0].type).to.eql('argument');
expect(parse('{VAR}')[0].arg).to.eql('VAR');
});
});
describe("Selects", function() {
it("should accept a select statement based on a variable", function() {
expect(function(){ parse('{VAR, select, key{a} other{b}}'); }).to.not.throwError();
});
it("should be very whitespace agnostic", function(){
var firstRes = JSON.stringify(parse('{VAR, select, key{a} other{b}}'));
expect(JSON.stringify(parse('{VAR,select,key{a}other{b}}'))).to.eql(firstRes);
expect(JSON.stringify(parse('{ VAR , select , key {a} other {b} }'))).to.eql(firstRes);
expect(JSON.stringify(parse('{ \n VAR \n , \n select \n\n , \n \n key \n \n {a} \n other \n {b} \n \n }'))).to.eql(firstRes);
expect(JSON.stringify(parse('{ \t VAR \n , \n\t\r select \n\t , \t \n key \n \t {a} \n other \t {b} \t \t }'))).to.eql(firstRes);
});
it("should allow you to use MessageFormat extension keywords other places, including in select keys", function() {
// use `select` as a select key
expect(parse('x {TEST, select, select{a} other{b} }')
[1].cases[0].key
).to.eql('select');
// use `offset` as a key (since it goes here in a `plural` case)
expect(parse('x {TEST, select, offset{a} other{b} }')
[1].cases[0].key
).to.eql('offset');
// use the exact variable name as a key name
expect(parse('x {TEST, select, TEST{a} other{b} }')
[1].cases[0].key
).to.eql('TEST');
});
it("should be case-sensitive (select keyword is lowercase, everything else doesn't matter)", function() {
expect(function(){ var a = parse('{TEST, Select, a{a} other{b}}'); }).to.throwError();
expect(function(){ var a = parse('{TEST, SELECT, a{a} other{b}}'); }).to.throwError();
expect(function(){ var a = parse('{TEST, selecT, a{a} other{b}}'); }).to.throwError();
});
it("should not accept keys with `=` prefixes", function() {
expect(function(){ var a = parse('{TEST, select, =0{a} other{b}}'); }).to.throwError();
});
});
describe("React", function() {
it("should accept a react statement based on a variable", function() {
expect(function(){ parse('{a, react}'); }).to.not.throwError();
expect(function(){ parse('{a, react, href {hello} }'); }).to.not.throwError();
});
});
describe("Plurals", function() {
it("should accept a variable, no offset, and plural keys", function() {
expect(function(){ var a = parse('{NUM, plural, one{1} other{2}}'); }).to.not.throwError();
});
it("should accept exact values with `=` prefixes", function() {
expect(
parse('{NUM, plural, =0{e1} other{2}}')[0].cases[0].key
).to.eql(0);
expect(
parse('{NUM, plural, =1{e1} other{2}}')[0].cases[0].key
).to.eql(1);
expect(
parse('{NUM, plural, =2{e1} other{2}}')[0].cases[0].key
).to.eql(2);
expect(
parse('{NUM, plural, =1{e1} other{2}}')[0].cases[1].key
).to.eql("other");
});
it("should accept the 6 official keywords", function() {
// 'zero', 'one', 'two', 'few', 'many' and 'other'
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[0].key
).to.eql('zero');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[0].key
).to.eql('zero');
expect(
parse('{NUM, plural,zero {0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[0].key
).to.eql('zero');
expect(
parse('{NUM, plural, \nzero\n {0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[0].key
).to.eql('zero');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[1].key
).to.eql('one');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[2].key
).to.eql('two');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[3].key
).to.eql('few');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[4].key
).to.eql('many');
expect(
parse('{NUM, plural, zero{0} one{1} two{2} few{5} many{100} other{101}}')[0].cases[5].key
).to.eql('other');
});
it("should be gracious with whitespace", function() {
var firstRes = JSON.stringify(parse('{NUM, plural, one{1} other{2}}'));
expect(JSON.stringify(parse('{ NUM, plural, one{1} other{2} }'))).to.eql(firstRes);
expect(JSON.stringify(parse('{NUM,plural,one{1}other{2}}'))).to.eql(firstRes);
expect(JSON.stringify(parse('{\nNUM, \nplural,\n one\n\n{1}\n other {2}\n\n\n}'))).to.eql(firstRes);
expect(JSON.stringify(parse('{\tNUM\t,\t\t\r plural\t\n, \tone\n{1} other\t\n{2}\n\n\n}'))).to.eql(firstRes);
});
it("should take an offset", function() {
expect(parse('{NUM, plural, offset:4 other{a}}')).to.be.ok();
expect(parse('{NUM, plural, offset : 4 other{a}}')).to.be.ok();
expect(parse('{NUM, plural, offset\n\t\r : \t\n\r4 other{a}}')).to.be.ok();
// technically this is parsable since js identifiers don't start with numbers
expect(parse('{NUM,plural,offset:4other{a}}')).to.be.ok();
expect(
parse('{NUM, plural, offset:4 other{a}}')[0].offset
).to.eql(4);
expect(
parse('{NUM,plural,offset:4other{a}}')[0].offset
).to.eql(4);
});
it("should support quoting", function() {
expect(parse("{NUM, plural, one{{x,date,y-M-dd # '#'}} two{two}}")[0].cases[0].tokens).to.eql([{
type: 'function', arg: 'x', key: 'date',
param: {
tokens: [ 'y-M-dd ', { type: 'octothorpe' }, ' #' ]
}
}]);
expect(parse("{NUM, plural, one{# '' #} two{two}}")[0].cases[0].tokens).to.eql([
{ type: 'octothorpe' }, " ' ", { type: 'octothorpe' }
]);
expect(parse("{NUM, plural, one{# '#'} two{two}}")[0].cases[0].tokens).to.eql([
{ type: 'octothorpe' }, ' #'
]);
expect(parse("{NUM, plural, one{one#} two{two}}")[0].cases[0].tokens).to.eql([
'one', { type: 'octothorpe' }
]);
})
describe('options.strict', function() {
var src = "{NUM, plural, one{# {VAR,select,key{# '#' one#}}} two{two}}";
it('should parse # correctly without strict option', function() {
expect(parse(src)[0].cases[0].tokens[2].cases[0].tokens).to.eql([
{ type: 'octothorpe' }, ' # one', { type: 'octothorpe' }
]);
})
it('should parse # correctly with strict option', function() {
expect(parse(src, { strict: true })[0].cases[0].tokens[2].cases[0].tokens).to.eql([
"# '#' one#"
]);
})
})
});
describe("Ordinals", function() {
it("should accept a variable and ordinal keys", function() {
expect(function(){ var a = parse('{NUM, selectordinal, one{1} other{2}}'); }).to.not.throwError();
});
it("should accept exact values with `=` prefixes", function() {
expect(
parse('{NUM, selectordinal, =0{e1} other{2}}')[0].cases[0].key
).to.eql(0);
expect(
parse('{NUM, selectordinal, =1{e1} other{2}}')[0].cases[0].key
).to.eql(1);
expect(
parse('{NUM, selectordinal, =2{e1} other{2}}')[0].cases[0].key
).to.eql(2);
expect(
parse('{NUM, selectordinal, =1{e1} other{2}}')[0].cases[1].key
).to.eql("other");
});
});
describe("Functions", function() {
it("should require lower-case type", function() {
expect(function(){ parse('{var,date}'); }).to.not.throwError();
expect(function(){ parse('{var,Date}'); }).to.throwError();
expect(function(){ parse('{var,daTe}'); }).to.throwError();
expect(function(){ parse('{var,9ate}'); }).to.throwError();
})
it('should be gracious with whitespace around arg and key', function() {
var expected = { type: 'function', arg: 'var', key: 'date', param: null }
expect(parse('{var,date}')[0]).to.eql(expected);
expect(parse('{var, date}')[0]).to.eql(expected);
expect(parse('{ var, date }')[0]).to.eql(expected);
expect(parse('{\nvar, \ndate\n}')[0]).to.eql(expected);
})
it("should accept parameters", function() {
expect(parse('{var,date,long}')[0]).to.eql({
type: 'function', arg: 'var', key: 'date', param: { tokens: ['long'] }
});
expect(parse('{var,date,long,short}')[0].param.tokens).to.eql(['long,short']);
})
it("should accept parameters with whitespace", function() {
expect(parse('{var,date,y-M-d HH:mm:ss zzzz}')[0]).to.eql({
type: 'function', arg: 'var', key: 'date', param: { tokens: ['y-M-d HH:mm:ss zzzz'] }
});
expect(parse('{var,date, y-M-d HH:mm:ss zzzz }')[0].param.tokens).to.eql([' y-M-d HH:mm:ss zzzz ']);
})
it("should accept parameters with special characters", function() {
expect(parse("{var,date,y-M-d '{,}' '' HH:mm:ss zzzz}")[0]).to.eql({
type: 'function', arg: 'var', key: 'date', param: { tokens: [ 'y-M-d {,} \' HH:mm:ss zzzz' ] }
});
expect(parse("{var,date,y-M-d '{,}' '' HH:mm:ss zzzz'}'}")[0].param.tokens).to.eql(["y-M-d {,} ' HH:mm:ss zzzz}"]);
expect(parse("{var,date,y-M-d # HH:mm:ss zzzz}")[0].param.tokens).to.eql(["y-M-d # HH:mm:ss zzzz"]);
expect(parse("{var,date,y-M-d '#' HH:mm:ss zzzz}")[0].param.tokens).to.eql(["y-M-d '#' HH:mm:ss zzzz"]);
expect(parse("{var,date,y-M-d, HH:mm:ss zzzz}")[0].param.tokens).to.eql(["y-M-d, HH:mm:ss zzzz"]);
})
it('should accept parameters containing a basic variable', function() {
expect(parse('{foo, date, {bar}}')[0]).to.eql({
type: 'function',
arg: 'foo',
key: 'date',
param: { tokens: [' ', { arg: 'bar', type: 'argument' }] }
})
})
it('should accept parameters containing a select', function() {
expect(parse('{foo, date, {bar, select, other{baz}}}')[0]).to.eql({
type: 'function',
arg: 'foo',
key: 'date',
param: { tokens: [' ', {
arg: 'bar',
type: 'select',
cases: [{ key: 'other', tokens: ['baz'] }]
}] }
})
})
it('should accept parameters containing a plural', function() {
expect(parse('{foo, date, {bar, plural, other{#}}}')[0]).to.eql({
type: 'function',
arg: 'foo',
key: 'date',
param: { tokens: [' ', {
arg: 'bar',
type: 'plural',
offset: 0,
cases: [{ key: 'other', tokens: [{ type: 'octothorpe' }] }]
}] }
})
})
describe('options.strict', function() {
it('should require known function key with strict option', function() {
expect(function() { parse('{foo, bar}') }).to.not.throwError()
expect(function() { parse('{foo, bar}', { strict: true }) }).to.throwError()
expect(function() { parse('{foo, date}', { strict: true }) }).to.not.throwError()
})
it('parameter parsing should obey strict option', function() {
expect(parse("{foo, date, {bar'}', quote'', other{#}}}", { strict: true })[0]).to.eql({
type: 'function',
arg: 'foo',
key: 'date',
param: { tokens: [" {bar}, quote', other{#}}"] }
})
})
it('should require matched braces in parameter if strict option is set', function() {
expect(function() {
parse("{foo, date, {bar{}}", { strict: true })
}).to.throwError();
})
})
});
describe("Nested/Recursive blocks", function() {
it("should allow a select statement inside of a select statement", function() {
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, select, other{a}}}}'); }).to.not.throwError();
expect(
parse('{NUM1, select, other{{NUM2, select, other{a}}}}')
[0].cases[0].tokens[0].cases[0].tokens[0]
).to.eql('a');
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, select, other{{NUM3, select, other{b}}}}}}'); }).to.not.throwError();
expect(
parse('{NUM1, select, other{{NUM2, select, other{{NUM3, select, other{b}}}}}}')
[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0]
).to.eql('b');
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, select, other{{NUM3, select, other{{NUM4, select, other{c}}}}}}}}'); }).to.not.throwError();
expect(
parse('{NUM1, select, other{{NUM2, select, other{{NUM3, select, other{{NUM4, select, other{c}}}}}}}}')
[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0]
).to.eql('c');
});
it("should allow nested plural statements - with and without offsets", function() {
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
// ok we get it, it's recursive.
expect(
parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}')
[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0]
).to.eql('c');
});
it("should allow nested plural and select statements - with and without offsets", function() {
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, select, other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, offset:1 other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{a}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, select, other{{NUM3, select, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, offset:1 other{{NUM3, select, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, offset:1 other{{NUM3, select, other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{b}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, offset:1 other{{NUM4, plural, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, select, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, offset:1 other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, select, other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, plural, other{{NUM2, plural, offset:1 other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
expect(function(){ var a = parse('{NUM1, select, other{{NUM2, select, other{{NUM3, plural, offset:1 other{{NUM4, plural, offset:1 other{c}}}}}}}}'); }).to.not.throwError();
// ok we get it, it's recursive.
expect(
parse('{NUM1, selectordinal, other{{NUM2, plural, offset:1 other{{NUM3, selectordinal, other{{NUM4, plural, offset:1 other{c}}}}}}}}')
[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0].cases[0].tokens[0]
).to.eql('c');
});
});
describe("Errors", function() {
it("should catch mismatched/invalid bracket situations", function() {
expect(function(){ parse('}'); }).to.throwError();
expect(function(){ parse('{'); }).to.throwError();
expect(function(){ parse('{{X}'); }).to.throwError();
expect(function(){ parse('{}'); }).to.throwError();
expect(function(){ parse('{}{'); }).to.throwError();
expect(function(){ parse('{X}{'); }).to.throwError();
expect(function(){ parse('}{}'); }).to.throwError();
expect(function(){ parse('}{X}'); }).to.throwError();
expect(function(){ parse('{}}'); }).to.throwError();
expect(function(){ parse('{X}}'); }).to.throwError();
expect(function(){ parse('{{X}}'); }).to.throwError();
expect(function(){ parse(); }).to.throwError();
// Technically an empty string is valid.
expect(function(){ parse(''); }).to.not.throwError();
});
it("should not allow an offset for SELECTs", function() {
expect(function(){ parse('{NUM, select, offset:1 test { 1 } test2 { 2 }}'); }).to.throwError();
});
it("should not allow invalid keys for PLURALs", function() {
expect(function(){ parse('{NUM, plural, one { 1 } invalid { error } other { 2 }}'); }).to.throwError();
expect(function(){ parse('{NUM, plural, one { 1 } some { error } other { 2 }}', { cardinal: ['one', 'other'] }); }).to.throwError();
});
it("should not allow invalid keys for SELECTORDINALs", function() {
expect(function(){ parse('{NUM, selectordinal, one { 1 } invalid { error } other { 2 }}'); }).to.throwError();
expect(function(){ parse('{NUM, selectordinal, one { 1 } some { error } other { 2 }}', { ordinal: ['one', 'other'] }); }).to.throwError();
});
it("should allow an offset for SELECTORDINALs", function() {
expect(function(){ parse('{NUM, selectordinal, offset:1 one { 1 } other { 2 }}'); }).to.not.throwError();
});
it("shouldn't allow characters in variables that aren't valid JavaScript identifiers", function() {
expect(function(){ parse('{☺}'); }).to.throwError();
expect(function(){ parse('{a☺}'); }).to.throwError();
});
it("should allow characters in variables that are valid ICU identifiers", function() {
expect(function(){ parse('{ű\u3000á}'); }).to.not.throwError();
});
it("should allow positional variables", function() {
expect(function(){ parse('{0}'); }).to.not.throwError();
});
it("should throw errors on negative offsets", function() {
expect(function(){ parse('{NUM, plural, offset:-4 other{a}}'); }).to.throwError();
});
});