| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// Tests for the MessageFormat class. |
| /// |
| /// Currently, these tests are the ones directly ported from Closure. |
| |
| import 'package:intl/message_format.dart'; |
| import 'package:test/test.dart'; |
| |
| void main() { |
| test('testEmptyPattern', () { |
| var fmt = MessageFormat(''); |
| expect(fmt.format({}), ''); |
| }); |
| |
| test('testMissingLeftCurlyBrace', () { |
| expect(() { |
| var fmt = MessageFormat('\'\'{}}'); |
| fmt.format({}); |
| }, throwsA(predicate((e) { |
| return e is AssertionError && e.message == 'No matching { for }.'; |
| }))); |
| }); |
| |
| test('testTooManyLeftCurlyBraces', () { |
| expect(() { |
| var fmt = MessageFormat('{} {'); |
| fmt.format({}); |
| }, throwsA(predicate((e) { |
| return e is AssertionError && |
| e.message == 'There are mismatched { or } in the pattern.'; |
| }))); |
| }); |
| |
| test('testSimpleReplacement', () { |
| var fmt = MessageFormat('New York in {SEASON} is nice.'); |
| expect(fmt.format({'SEASON': 'the Summer'}), |
| 'New York in the Summer is nice.'); |
| }); |
| |
| test('testSimpleSelect', () { |
| var fmt = MessageFormat('{GENDER, select,' |
| 'male {His}' |
| 'female {Her}' |
| 'other {Its}}' |
| ' bicycle is {GENDER, select, male {blue} female {red} other {green}}.'); |
| |
| expect(fmt.format({'GENDER': 'male'}), 'His bicycle is blue.'); |
| expect(fmt.format({'GENDER': 'female'}), 'Her bicycle is red.'); |
| expect(fmt.format({'GENDER': 'other'}), 'Its bicycle is green.'); |
| expect(fmt.format({'GENDER': 'whatever'}), 'Its bicycle is green.'); |
| }); |
| |
| test('testSimplePlural', () { |
| var fmt = MessageFormat('I see {NUM_PEOPLE, plural, offset:1 ' |
| '=0 {no one at all in {PLACE}.} ' |
| '=1 {{PERSON} in {PLACE}.} ' |
| 'one {{PERSON} and one other person in {PLACE}.} ' |
| 'other {{PERSON} and # other people in {PLACE}.}}'); |
| |
| expect(fmt.format({'NUM_PEOPLE': 0, 'PLACE': 'Belgrade'}), |
| 'I see no one at all in Belgrade.'); |
| expect(fmt.format({'NUM_PEOPLE': 1, 'PERSON': 'Markus', 'PLACE': 'Berlin'}), |
| 'I see Markus in Berlin.'); |
| expect(fmt.format({'NUM_PEOPLE': 2, 'PERSON': 'Mark', 'PLACE': 'Athens'}), |
| 'I see Mark and one other person in Athens.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 100, 'PERSON': 'Cibu', 'PLACE': 'the cubes'}), |
| 'I see Cibu and 99 other people in the cubes.'); |
| }); |
| |
| test('testSimplePluralNoOffset', () { |
| var fmt = MessageFormat('I see {NUM_PEOPLE, plural, ' |
| '=0 {no one at all} ' |
| '=1 {{PERSON}} ' |
| 'one {{PERSON} and one other person} ' |
| 'other {{PERSON} and # other people}} in {PLACE}.'); |
| |
| expect(fmt.format({'NUM_PEOPLE': 0, 'PLACE': 'Belgrade'}), |
| 'I see no one at all in Belgrade.'); |
| expect(fmt.format({'NUM_PEOPLE': 1, 'PERSON': 'Markus', 'PLACE': 'Berlin'}), |
| 'I see Markus in Berlin.'); |
| expect(fmt.format({'NUM_PEOPLE': 2, 'PERSON': 'Mark', 'PLACE': 'Athens'}), |
| 'I see Mark and 2 other people in Athens.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 100, 'PERSON': 'Cibu', 'PLACE': 'the cubes'}), |
| 'I see Cibu and 100 other people in the cubes.'); |
| }); |
| |
| test('testSelectNestedInPlural', () { |
| var fmt = MessageFormat('{CIRCLES, plural, ' |
| 'one {{GENDER, select, ' |
| ' female {{WHO} added you to her circle} ' |
| ' other {{WHO} added you to his circle}}} ' |
| 'other {{GENDER, select, ' |
| ' female {{WHO} added you to her # circles} ' |
| ' other {{WHO} added you to his # circles}}}}'); |
| |
| expect(fmt.format({'GENDER': 'female', 'WHO': 'Jelena', 'CIRCLES': 1}), |
| 'Jelena added you to her circle'); |
| expect(fmt.format({'GENDER': 'male', 'WHO': 'Milan', 'CIRCLES': 1234}), |
| 'Milan added you to his 1,234 circles'); |
| }); |
| |
| test('testPluralNestedInSelect', () { |
| // Added offset just for testing purposes. It doesn't make sense |
| // to have it otherwise. |
| var fmt = MessageFormat('{GENDER, select, ' |
| 'female {{NUM_GROUPS, plural, ' |
| ' one {{WHO} added you to her group} ' |
| ' other {{WHO} added you to her # groups}}} ' |
| 'other {{NUM_GROUPS, plural, offset:1' |
| ' one {{WHO} added you to his group} ' |
| ' other {{WHO} added you to his # groups}}}}'); |
| |
| expect(fmt.format({'GENDER': 'female', 'WHO': 'Jelena', 'NUM_GROUPS': 1}), |
| 'Jelena added you to her group'); |
| expect(fmt.format({'GENDER': 'male', 'WHO': 'Milan', 'NUM_GROUPS': 1234}), |
| 'Milan added you to his 1,233 groups'); |
| }); |
| |
| test('testLiteralOpenCurlyBrace', () { |
| var fmt = MessageFormat( |
| "Anna's house has '{0} and # in the roof' and {NUM_COWS} cows."); |
| expect(fmt.format({'NUM_COWS': '5'}), |
| "Anna's house has {0} and # in the roof and 5 cows."); |
| }); |
| |
| test('testLiteralClosedCurlyBrace', () { |
| var fmt = MessageFormat( |
| 'Anna\'s house has \'{\'0\'} and # in the roof\' and {NUM_COWS} cows.'); |
| expect(fmt.format({'NUM_COWS': '5'}), |
| 'Anna\'s house has {0} and # in the roof and 5 cows.'); |
| // Regression for Closure implementation bug: b/34764827 |
| expect(fmt.format({'NUM_COWS': '8'}), |
| 'Anna\'s house has {0} and # in the roof and 8 cows.'); |
| }); |
| |
| test('testLiteralPoundSign', () { |
| var fmt = MessageFormat( |
| "Anna's house has '{0}' and '# in the roof' and {NUM_COWS} cows."); |
| expect(fmt.format({'NUM_COWS': '5'}), |
| "Anna's house has {0} and # in the roof and 5 cows."); |
| // Regression for: b/34764827 |
| expect(fmt.format({'NUM_COWS': '10'}), |
| 'Anna\'s house has {0} and # in the roof and 10 cows.'); |
| }); |
| |
| test('testNoLiteralsForSingleQuotes', () { |
| var fmt = MessageFormat("Anna's house 'has {NUM_COWS} cows'."); |
| expect(fmt.format({'NUM_COWS': '5'}), "Anna's house 'has 5 cows'."); |
| }); |
| |
| test('testConsecutiveSingleQuotesAreReplacedWithOneSingleQuote', () { |
| var fmt = MessageFormat("Anna''s house a'{''''b'"); |
| expect(fmt.format({}), "Anna's house a{''b"); |
| }); |
| |
| test('testConsecutiveSingleQuotesBeforeSpecialCharDontCreateLiteral', () { |
| var fmt = MessageFormat("a''{NUM_COWS}'b"); |
| expect(fmt.format({'NUM_COWS': '5'}), "a'5'b"); |
| }); |
| |
| test('testSerbianSimpleSelect', () { |
| var fmt = MessageFormat( |
| '{GENDER, select, female {Njen} other {Njegov}} bicikl je ' |
| '{GENDER, select, female {crven} other {plav}}.', |
| locale: 'sr'); |
| |
| expect(fmt.format({'GENDER': 'male'}), 'Njegov bicikl je plav.'); |
| expect(fmt.format({'GENDER': 'female'}), 'Njen bicikl je crven.'); |
| }); |
| |
| test('testSerbianSimplePlural', () { |
| var fmt = MessageFormat( |
| 'Ja {NUM_PEOPLE, plural, offset:1 ' |
| ' =0 {ne vidim nikoga} ' |
| ' =1 {vidim {PERSON}} ' |
| ' one {vidim {PERSON} i jos # osobu} ' |
| ' few {vidim {PERSON} i jos # osobe} ' |
| ' many {vidim {PERSON} i jos # osoba} ' |
| ' other {vidim {PERSON} i jos # osoba}} ' |
| 'u {PLACE}.', |
| locale: 'sr'); |
| |
| expect(fmt.format({'NUM_PEOPLE': 0, 'PLACE': 'Beogradu'}), |
| 'Ja ne vidim nikoga u Beogradu.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 1, 'PERSON': 'Markusa', 'PLACE': 'Berlinu'}), |
| 'Ja vidim Markusa u Berlinu.'); |
| expect(fmt.format({'NUM_PEOPLE': 2, 'PERSON': 'Marka', 'PLACE': 'Atini'}), |
| 'Ja vidim Marka i jos 1 osobu u Atini.'); |
| expect(fmt.format({'NUM_PEOPLE': 4, 'PERSON': 'Petra', 'PLACE': 'muzeju'}), |
| 'Ja vidim Petra i jos 3 osobe u muzeju.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 100, 'PERSON': 'Cibua', 'PLACE': 'bazenu'}), |
| 'Ja vidim Cibua i jos 99 osoba u bazenu.'); |
| }); |
| |
| test('testSerbianSimplePluralNoOffset', () { |
| var fmt = MessageFormat( |
| 'Ja {NUM_PEOPLE, plural, ' |
| ' =0 {ne vidim nikoga} ' |
| ' =1 {vidim {PERSON}} ' |
| ' one {vidim {PERSON} i jos # osobu} ' |
| ' few {vidim {PERSON} i jos # osobe} ' |
| ' many {vidim {PERSON} i jos # osoba} ' |
| ' other {vidim {PERSON} i jos # osoba}} ' |
| 'u {PLACE}.', |
| locale: 'sr'); |
| |
| expect(fmt.format({'NUM_PEOPLE': 0, 'PLACE': 'Beogradu'}), |
| 'Ja ne vidim nikoga u Beogradu.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 1, 'PERSON': 'Markusa', 'PLACE': 'Berlinu'}), |
| 'Ja vidim Markusa u Berlinu.'); |
| expect(fmt.format({'NUM_PEOPLE': 21, 'PERSON': 'Marka', 'PLACE': 'Atini'}), |
| 'Ja vidim Marka i jos 21 osobu u Atini.'); |
| expect(fmt.format({'NUM_PEOPLE': 3, 'PERSON': 'Petra', 'PLACE': 'muzeju'}), |
| 'Ja vidim Petra i jos 3 osobe u muzeju.'); |
| expect( |
| fmt.format({'NUM_PEOPLE': 100, 'PERSON': 'Cibua', 'PLACE': 'bazenu'}), |
| 'Ja vidim Cibua i jos 100 osoba u bazenu.'); |
| }); |
| |
| test('testSerbianSelectNestedInPlural', () { |
| var fmt = MessageFormat( |
| '{CIRCLES, plural, ' |
| ' one {{GENDER, select, ' |
| ' female {{WHO} vas je dodala u njen # kruzok} ' |
| ' other {{WHO} vas je dodao u njegov # kruzok}}} ' |
| ' few {{GENDER, select, ' |
| ' female {{WHO} vas je dodala u njena # kruzoka} ' |
| ' other {{WHO} vas je dodao u njegova # kruzoka}}} ' |
| ' many {{GENDER, select, ' |
| ' female {{WHO} vas je dodala u njenih # kruzoka} ' |
| ' other {{WHO} vas je dodao u njegovih # kruzoka}}} ' |
| ' other {{GENDER, select, ' |
| ' female {{WHO} vas je dodala u njenih # kruzoka} ' |
| ' other {{WHO} vas je dodao u njegovih # kruzoka}}}}', |
| locale: 'hr'); |
| |
| expect(fmt.format({'GENDER': 'female', 'WHO': 'Jelena', 'CIRCLES': 21}), |
| 'Jelena vas je dodala u njen 21 kruzok'); |
| expect(fmt.format({'GENDER': 'female', 'WHO': 'Jelena', 'CIRCLES': 3}), |
| 'Jelena vas je dodala u njena 3 kruzoka'); |
| expect(fmt.format({'GENDER': 'female', 'WHO': 'Jelena', 'CIRCLES': 5}), |
| 'Jelena vas je dodala u njenih 5 kruzoka'); |
| expect(fmt.format({'GENDER': 'male', 'WHO': 'Milan', 'CIRCLES': 1235}), |
| 'Milan vas je dodao u njegovih 1.235 kruzoka'); |
| }); |
| |
| test('testFallbackToOtherOptionInPlurals', () { |
| // Use Arabic plural rules since they have all six cases. |
| // Only locale and numbers matter, the actual language of the message |
| // does not. |
| var fmt = |
| MessageFormat('{NUM_MINUTES, plural, other {# minutes}}', locale: 'ar'); |
| |
| // These numbers exercise all cases for the arabic plural rules. |
| expect(fmt.format({'NUM_MINUTES': 0}), '0 minutes'); |
| expect(fmt.format({'NUM_MINUTES': 1}), '1 minutes'); |
| expect(fmt.format({'NUM_MINUTES': 2}), '2 minutes'); |
| expect(fmt.format({'NUM_MINUTES': 3}), '3 minutes'); |
| expect(fmt.format({'NUM_MINUTES': 11}), '11 minutes'); |
| expect(fmt.format({'NUM_MINUTES': 1.5}), '1.5 minutes'); |
| }); |
| |
| test('testPoundShowsNumberMinusOffsetInAllCases', () { |
| var fmt = MessageFormat( |
| '{SOME_NUM, plural, offset:1 =0 {#} =1 {#} =2 {#} one {#} other {#}}'); |
| |
| expect(fmt.format({'SOME_NUM': '0'}), '-1'); |
| expect(fmt.format({'SOME_NUM': '1'}), '0'); |
| expect(fmt.format({'SOME_NUM': '2'}), '1'); |
| expect(fmt.format({'SOME_NUM': '21'}), '20'); |
| }); |
| |
| test('testSpecialCharactersInParamaterDontChangeFormat', () { |
| var fmt = MessageFormat('{SOME_NUM, plural, other {# {GROUP}}}'); |
| |
| // Test pound sign. |
| expect(fmt.format({'SOME_NUM': '10', 'GROUP': 'group#1'}), '10 group#1'); |
| // Test other special characters in parameters, like { and }. |
| expect(fmt.format({'SOME_NUM': '10', 'GROUP': '} {'}), '10 } {'); |
| }); |
| |
| test('testMissingOrInvalidPluralParameter', () { |
| var fmt = MessageFormat('{SOME_NUM, plural, other {result}}'); |
| |
| // Key name doesn't match A != SOME_NUM. |
| expect(fmt.format({'A': '10'}), 'Undefined parameter - SOME_NUM'); |
| |
| // Value is not a number. |
| expect(fmt.format({'SOME_NUM': 'Value'}), 'Invalid parameter - SOME_NUM'); |
| }); |
| |
| test('testMissingSelectParameter', () { |
| var fmt = MessageFormat('{GENDER, select, other {result}}'); |
| |
| // Key name doesn't match A != GENDER. |
| expect(fmt.format({'A': 'female'}), 'Undefined parameter - GENDER'); |
| }); |
| |
| test('testMissingSimplePlaceholder', () { |
| var fmt = MessageFormat('{result}'); |
| |
| // Key name doesn't match A != result. |
| expect(fmt.format({'A': 'none'}), 'Undefined parameter - result'); |
| }); |
| |
| test('testPlural', () { |
| var fmt = MessageFormat( |
| '{SOME_NUM, plural,' |
| ' =0 {none}' |
| ' =1 {exactly one}' |
| ' one {# one}' |
| ' few {# few}' |
| ' many {# many}' |
| ' other {# other}' |
| '}', |
| locale: 'ru'); |
| |
| expect(fmt.format({'SOME_NUM': 0}), 'none'); |
| expect(fmt.format({'SOME_NUM': 1}), 'exactly one'); |
| expect(fmt.format({'SOME_NUM': 21}), '21 one'); |
| expect(fmt.format({'SOME_NUM': 23}), '23 few'); |
| expect(fmt.format({'SOME_NUM': 17}), '17 many'); |
| expect(fmt.format({'SOME_NUM': 100}), '100 many'); |
| expect(fmt.format({'SOME_NUM': 1.4}), '1,4 other'); |
| expect(fmt.format({'SOME_NUM': '10.0'}), '10 many'); |
| expect(fmt.format({'SOME_NUM': '100.00'}), '100 many'); |
| }); |
| |
| test('testPluralWithIgnorePound', () { |
| var fmt = MessageFormat('{SOME_NUM, plural, other {# {GROUP}}}'); |
| |
| // Test pound sign. |
| expect(fmt.formatIgnoringPound({'SOME_NUM': '10', 'GROUP': 'group#1'}), |
| '# group#1'); |
| // Test other special characters in parameters, like { and }. |
| expect( |
| fmt.formatIgnoringPound({'SOME_NUM': '10', 'GROUP': '} {'}), '# } {'); |
| }); |
| |
| test('testSimplePluralWithIgnorePound', () { |
| var fmt = MessageFormat('I see {NUM_PEOPLE, plural, offset:1 ' |
| '=0 {no one at all in {PLACE}.} ' |
| '=1 {{PERSON} in {PLACE}.} ' |
| 'one {{PERSON} and one other person in {PLACE}.} ' |
| 'other {{PERSON} and # other people in {PLACE}.}}'); |
| |
| expect( |
| fmt.formatIgnoringPound( |
| {'NUM_PEOPLE': 100, 'PERSON': 'Cibu', 'PLACE': 'the cubes'}), |
| 'I see Cibu and # other people in the cubes.'); |
| }); |
| |
| test('testRomanianOffsetWithNegativeValue', () { |
| var fmt = MessageFormat( |
| '{NUM_FLOOR, plural, offset:2 ' |
| 'one {One #}' |
| 'few {Few #}' |
| 'other {Other #}}', |
| locale: 'ro'); |
| |
| // Checking that the decision is done after the offset is substracted |
| expect(fmt.format({'NUM_FLOOR': -1}), 'Few -3'); |
| expect(fmt.format({'NUM_FLOOR': 1}), 'One -1'); |
| expect(fmt.format({'NUM_FLOOR': -3}), 'Few -5'); |
| expect(fmt.format({'NUM_FLOOR': 3}), 'One 1'); |
| expect(fmt.format({'NUM_FLOOR': -25}), 'Other -27'); |
| expect(fmt.format({'NUM_FLOOR': 25}), 'Other 23'); |
| }); |
| |
| ignoreTest('testSimpleOrdinal', () { |
| // TOFIX. Ordinal not supported in Dart |
| var fmt = MessageFormat('{NUM_FLOOR, selectordinal, ' |
| 'one {Take the elevator to the #st floor.}' |
| 'two {Take the elevator to the #nd floor.}' |
| 'few {Take the elevator to the #rd floor.}' |
| 'other {Take the elevator to the #th floor.}}'); |
| |
| expect(fmt.format({'NUM_FLOOR': 1}), 'Take the elevator to the 1st floor.'); |
| expect(fmt.format({'NUM_FLOOR': 2}), 'Take the elevator to the 2nd floor.'); |
| expect(fmt.format({'NUM_FLOOR': 3}), 'Take the elevator to the 3rd floor.'); |
| expect(fmt.format({'NUM_FLOOR': 4}), 'Take the elevator to the 4th floor.'); |
| expect( |
| fmt.format({'NUM_FLOOR': 23}), 'Take the elevator to the 23rd floor.'); |
| // Esoteric example. |
| expect(fmt.format({'NUM_FLOOR': 0}), 'Take the elevator to the 0th floor.'); |
| }); |
| |
| ignoreTest('testOrdinalWithNegativeValue', () { |
| // TOFIX. Ordinal not supported in Dart |
| var fmt = MessageFormat('{NUM_FLOOR, selectordinal, ' |
| 'one {Take the elevator to the #st floor.}' |
| 'two {Take the elevator to the #nd floor.}' |
| 'few {Take the elevator to the #rd floor.}' |
| 'other {Take the elevator to the #th floor.}}'); |
| |
| expect( |
| fmt.format({'NUM_FLOOR': -1}), 'Take the elevator to the -1st floor.'); |
| expect( |
| fmt.format({'NUM_FLOOR': -2}), 'Take the elevator to the -2nd floor.'); |
| expect( |
| fmt.format({'NUM_FLOOR': -3}), 'Take the elevator to the -3rd floor.'); |
| expect( |
| fmt.format({'NUM_FLOOR': -4}), 'Take the elevator to the -4th floor.'); |
| }); |
| |
| ignoreTest('testSimpleOrdinalWithIgnorePound', () { |
| // TOFIX. Ordinal not supported in Dart |
| var fmt = MessageFormat('{NUM_FLOOR, selectordinal, ' |
| 'one {Take the elevator to the #st floor.}' |
| 'two {Take the elevator to the #nd floor.}' |
| 'few {Take the elevator to the #rd floor.}' |
| 'other {Take the elevator to the #th floor.}}'); |
| |
| expect(fmt.formatIgnoringPound({'NUM_FLOOR': 100}), |
| 'Take the elevator to the #th floor.'); |
| }); |
| |
| ignoreTest('testMissingOrInvalidOrdinalParameter', () { |
| // TOFIX. Ordinal not supported in Dart |
| var fmt = MessageFormat('{SOME_NUM, selectordinal, other {result}}'); |
| |
| // Key name doesn't match A != SOME_NUM. |
| expect( |
| fmt.format({'A': '10'}), 'Undefined or invalid parameter - SOME_NUM'); |
| |
| // Value is not a number. |
| expect(fmt.format({'SOME_NUM': 'Value'}), |
| 'Undefined or invalid parameter - SOME_NUM'); |
| }); |
| } // end of main |
| |
| // Disabling unit tests without having to comment the whole body. |
| // Similar to @Ignore in JUnit |
| void ignoreTest(description, body) { |
| print('\u001b[93mTest ignored: $description\u001b[m'); |
| } |