diff --git a/test/.vscode/settings.json b/test/.vscode/settings.json new file mode 100644 index 000000000..aa1a5c466 --- /dev/null +++ b/test/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "json.schemas": [ + { + "fileMatch": [ + "tests/**/*.json" + ], + "url": "./schemas/v0/tests.schema.json" + } + ] +} diff --git a/test/README.md b/test/README.md index ac0aecf94..4d69851c2 100644 --- a/test/README.md +++ b/test/README.md @@ -1,37 +1,25 @@ -The files in this directory were originally copied from the [messageformat project](https://github.com/messageformat/messageformat/tree/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/__fixtures) +The tests in the `./tests/` directory were originally copied from the [messageformat project](https://github.com/messageformat/messageformat/tree/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/__fixtures) and are here relicensed by their original author (Eemeli Aro) under the Unicode License. These test files are intended to be useful for testing multiple different message processors in different ways: -- `syntax-errors.json` — An array of strings that should produce a Syntax Error when parsed. - -- `data-model-errors.json` - An object with string keys and arrays of strings as values, - where each key is the name of an error and its value is an array of strings that - should produce `error` when processed. - Error names are defined in ["MessageFormat 2.0 Errors"](../spec/errors.md) in the spec. - -- `test-core.json` — An array of test cases that do not depend on any registry definitions. - Each test may include some of the following fields: - - `src: string` (required) — The MF2 syntax source. - - `exp: string` (required) — The expected result of formatting the message to a string. - - `locale: string` — The locale to use for formatting. Defaults to 'en-US'. - - `params: Record` — Parameters to pass in to the formatter for resolving external variables. - - `parts: object[]` — The expected result of formatting the message to parts. - - `cleanSrc: string` — A normalixed form of `src`, for testing stringifiers. - - `errors: { type: string }[]` — The runtime errors expected to be emitted when formatting the message. - If `errors` is either absent or empty, the message must be formatted without errors. - - `only: boolean` — Normally not set. A flag to use during development to only run one or more specific tests. - -- `test-function.json` — An object with string keys and arrays of test cases as values, - using the same definition as for `test-core.json`. - The keys each correspond to a function that is used in the tests. - Since the behavior of built-in formatters is implementation-specific, - the `exp` field should generally be omitted, - except for error cases. - -TypeScript `.d.ts` files are included for `test-core.json` and `test-function.json` with the above definition. +- `syntax.json` — Test cases that do not depend on any registry definitions. + +- `syntax-errors.json` — Strings that should produce a Syntax Error when parsed. + +- `data-model-errors.json` - Strings that should produce Data Model Error when processed. + Error names are defined in ["MessageFormat 2.0 Errors"](../spec/errors.md) in the spec. + +- `functions/` — Test cases that correspond to built-in functions. + The behaviour of the built-in formatters is implementation-specific so the `exp` field is often + omitted and assertions are made on error cases. Some examples of test harnesses using these tests, from the source repository: + - [CST parse/stringify tests](https://github.com/messageformat/messageformat/blob/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/cst/cst.test.ts) - [Data model stringify tests](https://github.com/messageformat/messageformat/blob/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/data-model/stringify.test.ts) - [Formatting tests](https://github.com/messageformat/messageformat/blob/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/messageformat.test.ts) + +A [JSON schema](./schemas/) is included for the test files in this repository. + +For users of Visual Studio Code, a [settings file](./.vscode/settings.json) is included that enables schema validation while editing the test files. diff --git a/test/data-model-errors.json b/test/data-model-errors.json deleted file mode 100644 index 0a6bd6764..000000000 --- a/test/data-model-errors.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "Variant Key Mismatch": [ - ".match {$foo :x} * * {{foo}}", - ".match {$foo :x} {$bar :x} * {{foo}}" - ], - "Missing Fallback Variant": [ - ".match {:foo} 1 {{_}}", - ".match {:foo} other {{_}}", - ".match {:foo} {:bar} * 1 {{_}} 1 * {{_}}" - ], - "Missing Selector Annotation": [ - ".match {$foo} one {{one}} * {{other}}", - ".input {$foo} .match {$foo} one {{one}} * {{other}}", - ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}" - ], - "Duplicate Declaration": [ - ".input {$foo} .input {$foo} {{_}}", - ".input {$foo} .local $foo = {42} {{_}}", - ".local $foo = {42} .input {$foo} {{_}}", - ".local $foo = {:unknown} .local $foo = {42} {{_}}", - ".local $foo = {$bar} .local $bar = {42} {{_}}", - ".local $foo = {$foo} {{_}}", - ".local $foo = {$bar} .local $bar = {$baz} {{_}}", - ".local $foo = {$bar :func} .local $bar = {$baz} {{_}}", - ".local $foo = {42 :func opt=$foo} {{_}}", - ".local $foo = {42 :func opt=$bar} .local $bar = {42} {{_}}" - ], - "Duplicate Option Name": [ - "bad {:placeholder option=x option=x}", - "bad {:placeholder ns:option=x ns:option=y}" - ] -} diff --git a/test/schemas/v0/tests.schema.json b/test/schemas/v0/tests.schema.json new file mode 100644 index 000000000..e3f9e6e4d --- /dev/null +++ b/test/schemas/v0/tests.schema.json @@ -0,0 +1,390 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "title": "MessageFormat 2 data-driven tests", + "description": "This is the main schema for MessageFormat 2 test source data.", + "type": "object", + "additionalProperties": false, + "required": [ + "tests" + ], + "properties": { + "scenario": { + "type": "string", + "description": "Identifier for the tests in the file" + }, + "description": { + "type": "string", + "description": "Information about the test scenario." + }, + "defaultTestProperties": { + "$ref": "#/$defs/defaultTestProperties" + }, + "tests": { + "type": "array", + "items": { + "$ref": "#/$defs/test" + }, + "minItems": 1 + } + }, + "$comment": "This allOf specifies required test properties that allow a default. A value will be required in 'defaultTestProperties' if one is not provided for every individual test.", + "allOf": [ + { + "anyOf": [ + { + "properties": { + "defaultTestProperties": { + "required": [ + "locale" + ] + } + } + }, + { + "properties": { + "tests": { + "type": "array", + "items": { + "required": [ + "locale" + ] + } + } + } + } + ] + }, + { + "anyOf": [ + { + "properties": { + "defaultTestProperties": { + "required": [ + "src" + ] + } + } + }, + { + "properties": { + "tests": { + "type": "array", + "items": { + "required": [ + "src" + ] + } + } + } + } + ] + }, + { + "$comment": "Only one assertion is required. It doesn't matter which type.", + "anyOf": [ + { + "properties": { + "defaultTestProperties": { + "$ref": "#/$defs/anyExp" + } + } + }, + { + "properties": { + "tests": { + "type": "array", + "items": { + "$ref": "#/$defs/anyExp" + } + } + } + } + ] + } + ], + "$defs": { + "defaultTestProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "$ref": "#/$defs/locale" + }, + "src": { + "$ref": "#/$defs/src" + }, + "params": { + "$ref": "#/$defs/params" + }, + "exp": { + "$ref": "#/$defs/exp" + }, + "expCleanSrc": { + "$ref": "#/$defs/expCleanSrc" + }, + "expParts": { + "$ref": "#/$defs/expParts" + }, + "expErrors": { + "$ref": "#/$defs/expErrors" + } + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "description": { + "type": "string", + "description": "Information about the test." + }, + "locale": { + "$ref": "#/$defs/locale" + }, + "src": { + "$ref": "#/$defs/src" + }, + "params": { + "$ref": "#/$defs/params" + }, + "exp": { + "$ref": "#/$defs/exp" + }, + "expCleanSrc": { + "$ref": "#/$defs/expCleanSrc" + }, + "expParts": { + "$ref": "#/$defs/expParts" + }, + "expErrors": { + "$ref": "#/$defs/expErrors" + }, + "only": { + "type": "boolean", + "description": "Normally not set. A flag to use during development to only run one or more specific tests." + } + } + }, + "locale": { + "description": "The locale to use for formatting.", + "type": "string" + }, + "src": { + "description": "The MF2 syntax source.", + "type": "string" + }, + "params": { + "description": "Parameters to pass in to the formatter for resolving external variables.", + "type": "array", + "items": { + "$ref": "#/$defs/var" + } + }, + "var": { + "type": "object", + "oneOf": [ + { + "additionalProperties": false, + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string" + }, + "value": {} + } + }, + { + "additionalProperties": false, + "required": [ + "name", + "type", + "value" + ], + "properties": { + "name": { + "type": "string" + }, + "type": { + "const": "datetime" + }, + "value": { + "$comment": "Should be converted to a datetime.", + "type": "string" + } + } + } + ] + }, + "exp": { + "description": "The expected result of formatting the message to a string.", + "type": "string" + }, + "expCleanSrc": { + "description": "The expected normalized form of `src`, for testing stringifiers.", + "type": "string" + }, + "expParts": { + "description": "The expected result of formatting the message to parts.", + "type": "array", + "items": { + "oneOf": [ + { + "description": "Message literal part.", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "value" + ], + "properties": { + "type": { + "const": "literal" + }, + "value": { + "type": "string" + } + } + }, + { + "description": "Message markup part.", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "kind", + "name" + ], + "properties": { + "type": { + "const": "markup" + }, + "kind": { + "enum": [ + "open", + "standalone", + "close" + ] + }, + "source": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": { + "type": "object" + } + } + }, + { + "description": "Message expression part.", + "type": "object", + "required": [ + "type", + "source" + ], + "not": { + "required": [ + "parts", + "value" + ] + }, + "properties": { + "type": { + "type": "string" + }, + "source": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "source": { + "type": "string" + }, + "value": {} + }, + "required": [ + "type" + ] + } + }, + "value": {} + } + } + ] + } + }, + "expErrors": { + "description": "The runtime errors expected to be emitted when formatting the message. If expErrors is either absent or empty, the message must be formatted without errors.", + "type": [ + "array", + "boolean" + ], + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "enum": [ + "syntax-error", + "variant-key-mismatch", + "missing-fallback-variant", + "missing-selector-annotation", + "duplicate-declaration", + "duplicate-option-name", + "unresolved-variable", + "unknown-function", + "unsupported-expression", + "invalid-expression", + "operand-mismatch", + "unsupported-statement", + "selection-error", + "formatting-error", + "bad-input" + ] + } + } + } + }, + "anyExp": { + "anyOf": [ + { + "required": [ + "exp" + ] + }, + { + "required": [ + "expCleanSrc" + ] + }, + { + "required": [ + "expParts" + ] + }, + { + "required": [ + "expErrors" + ] + } + ] + } + } +} diff --git a/test/syntax-errors.json b/test/syntax-errors.json deleted file mode 100644 index fc4537131..000000000 --- a/test/syntax-errors.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - ".", - "{", - "}", - "{}", - "{{", - "{{}", - "{{}}}", - "{|foo| #markup}", - "{{missing end brace}", - "{{missing end braces", - "{{missing end {$braces", - "{{extra}} content", - "empty { } placeholder", - "missing space {42:func}", - "missing space {|foo|:func}", - "missing space {|foo|@bar}", - "missing space {:func@bar}", - "missing space {:func @bar@baz}", - "missing space {:func @bar=42@baz}", - "missing space {+reserved@bar}", - "missing space {&private@bar}", - "bad {:} placeholder", - "bad {\\u0000placeholder}", - "no-equal {|42| :number minimumFractionDigits 2}", - "bad {:placeholder option=}", - "bad {:placeholder option value}", - "bad {:placeholder option:value}", - "bad {:placeholder option}", - "bad {:placeholder:}", - "bad {::placeholder}", - "bad {:placeholder::foo}", - "bad {:placeholder option:=x}", - "bad {:placeholder :option=x}", - "bad {:placeholder option::x=y}", - "bad {$placeholder option}", - "bad {:placeholder @attribute=}", - "bad {:placeholder @attribute=@foo}", - "no {placeholder end", - "no {$placeholder end", - "no {:placeholder end", - "no {|placeholder| end", - "no {|literal} end", - "no {|literal or placeholder end", - ".local bar = {|foo|} {{_}}", - ".local #bar = {|foo|} {{_}}", - ".local $bar {|foo|} {{_}}", - ".local $bar = |foo| {{_}}", - ".match {#foo} * {{foo}}", - ".match {} * {{foo}}", - ".match {|foo| :x} {|bar| :x} ** {{foo}}", - ".match * {{foo}}", - ".match {|x| :x} * foo", - ".match {|x| :x} * {{foo}} extra", - ".match |x| * {{foo}}" -] diff --git a/test/test-core.json b/test/test-core.json deleted file mode 100644 index 0e7049fdb..000000000 --- a/test/test-core.json +++ /dev/null @@ -1,212 +0,0 @@ -[ - { "src": "hello", "exp": "hello" }, - { "src": "hello {world}", "exp": "hello world" }, - { - "src": "hello { world\t\n}", - "exp": "hello world", - "cleanSrc": "hello {world}" - }, - { - "src": "hello {\u3000world\r}", - "exp": "hello world", - "cleanSrc": "hello {world}" - }, - { "src": "hello {|world|}", "exp": "hello world" }, - { "src": "hello {||}", "exp": "hello " }, - { - "src": "hello {$place}", - "params": { "place": "world" }, - "exp": "hello world" - }, - { - "src": "hello {$place-.}", - "params": { "place-.": "world" }, - "exp": "hello world" - }, - { - "src": "hello {$place}", - "errors": [{ "type": "unresolved-var" }], - "exp": "hello {$place}" - }, - { - "src": "{$one} and {$two}", - "params": { "one": 1.3, "two": 4.2 }, - "exp": "1.3 and 4.2" - }, - { - "src": "{$one} et {$two}", - "locale": "fr", - "params": { "one": 1.3, "two": 4.2 }, - "exp": "1,3 et 4,2" - }, - { "src": ".local $foo = {bar} {{bar {$foo}}}", "exp": "bar bar" }, - { "src": ".local $foo = {|bar|} {{bar {$foo}}}", "exp": "bar bar" }, - { - "src": ".local $foo = {|bar|} {{bar {$foo}}}", - "params": { "foo": "foo" }, - "exp": "bar bar" - }, - { - "src": ".local $foo = {$bar} {{bar {$foo}}}", - "params": { "bar": "foo" }, - "exp": "bar foo" - }, - { - "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", - "params": { "baz": "foo" }, - "exp": "bar foo" - }, - { - "src": ".input {$foo} {{bar {$foo}}}", - "params": { "foo": "foo" }, - "exp": "bar foo" - }, - { - "src": ".input {$foo} .local $bar = {$foo} {{bar {$bar}}}", - "params": { "foo": "foo" }, - "exp": "bar foo" - }, - { - "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", - "params": { "baz": "foo" }, - "exp": "bar foo" - }, - { "src": ".local $x = {42} .local $y = {$x} {{{$x} {$y}}}", "exp": "42 42" }, - { - "src": "{#tag}", - "exp": "", - "parts": [{ "type": "markup", "kind": "open", "name": "tag" }] - }, - { - "src": "{#tag}content", - "exp": "content", - "parts": [ - { "type": "markup", "kind": "open", "name": "tag" }, - { "type": "literal", "value": "content" } - ] - }, - { - "src": "{#ns:tag}content{/ns:tag}", - "exp": "content", - "parts": [ - { "type": "markup", "kind": "open", "name": "ns:tag" }, - { "type": "literal", "value": "content" }, - { "type": "markup", "kind": "close", "name": "ns:tag" } - ] - }, - { - "src": "{/tag}content", - "exp": "content", - "parts": [ - { "type": "markup", "kind": "close", "name": "tag" }, - { "type": "literal", "value": "content" } - ] - }, - { - "src": "{#tag foo=bar}", - "exp": "", - "parts": [ - { - "type": "markup", - "kind": "open", - "name": "tag", - "options": { "foo": "bar" } - } - ] - }, - { - "src": "{#tag foo=bar/}", - "cleanSrc": "{#tag foo=bar /}", - "exp": "", - "parts": [ - { - "type": "markup", - "kind": "standalone", - "name": "tag", - "options": { "foo": "bar" } - } - ] - }, - { - "src": "{#tag a:foo=|foo| b:bar=$bar}", - "params": { "bar": "b a r" }, - "exp": "", - "parts": [ - { - "type": "markup", - "kind": "open", - "name": "tag", - "options": { "a:foo": "foo", "b:bar": "b a r" } - } - ] - }, - { - "src": "{/tag foo=bar}", - "exp": "", - "parts": [ - { - "type": "markup", - "kind": "close", - "name": "tag", - "options": { "foo": "bar" } - } - ] - }, - { - "src": "{42 @foo @bar=13}", - "exp": "42", - "parts": [{ "type": "string", "value": "42" }] - }, - { - "src": "{42 @foo=$bar}", - "exp": "42", - "parts": [{ "type": "string", "value": "42" }] - }, - { - "src": "foo {+reserved}", - "exp": "foo {+}", - "parts": [ - { "type": "literal", "value": "foo " }, - { "type": "fallback", "source": "+" } - ], - "errors": [{ "type": "unsupported-annotation" }] - }, - { - "src": "foo {&private}", - "exp": "foo {&}", - "parts": [ - { "type": "literal", "value": "foo " }, - { "type": "fallback", "source": "&" } - ], - "errors": [{ "type": "unsupported-annotation" }] - }, - { - "src": "foo {?reserved @a @b=$c}", - "exp": "foo {?}", - "parts": [ - { "type": "literal", "value": "foo " }, - { "type": "fallback", "source": "?" } - ], - "errors": [{ "type": "unsupported-annotation" }] - }, - { - "src": ".foo {42} {{bar}}", - "exp": "bar", - "parts": [{ "type": "literal", "value": "bar" }], - "errors": [{ "type": "unsupported-statement" }] - }, - { - "src": ".foo{42}{{bar}}", - "cleanSrc": ".foo {42} {{bar}}", - "exp": "bar", - "parts": [{ "type": "literal", "value": "bar" }], - "errors": [{ "type": "unsupported-statement" }] - }, - { - "src": ".foo |}lit{| {42}{{bar}}", - "cleanSrc": ".foo |}lit{| {42} {{bar}}", - "exp": "bar", - "parts": [{ "type": "literal", "value": "bar" }], - "errors": [{ "type": "unsupported-statement" }] - } -] diff --git a/test/test-core.json.d.ts b/test/test-core.json.d.ts deleted file mode 100644 index 3bff7634a..000000000 --- a/test/test-core.json.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type TestMessage = { - /** The MF2 message to be tested. */ - src: string; - /** The locale to use for formatting. Defaults to 'en-US'. */ - locale?: string; - /** Parameters to pass in to the formatter for resolving external variables. */ - params?: Record; - /** The expected result of formatting the message to a string. */ - exp: string; - /** The expected result of formatting the message to parts. */ - parts?: Array; - /** A normalixed form of `src`, for testing stringifiers. */ - cleanSrc?: string; - /** The runtime errors expected to be emitted when formatting the message. */ - errors?: Array<{ type: string }>; - /** Normally not set. A flag to use during development to only run one or more specific tests. */ - only?: boolean; -}; - -declare const data: TestMessage[]; -export default data; diff --git a/test/test-functions.json b/test/test-functions.json deleted file mode 100644 index 03080a2b6..000000000 --- a/test/test-functions.json +++ /dev/null @@ -1,322 +0,0 @@ -{ - "date": [ - { "src": "{:date}", "exp": "{:date}", "errors": [{ "type": "bad-input" }] }, - { - "src": "{horse :date}", - "exp": "{|horse|}", - "errors": [{ "type": "bad-input" }] - }, - { "src": "{|2006-01-02| :date}" }, - { "src": "{|2006-01-02T15:04:06| :date}" }, - { "src": "{|2006-01-02| :date style=long}" }, - { - "src": ".local $d = {|2006-01-02| :date style=long} {{{$d :date}}}" - }, - { - "src": ".local $t = {|2006-01-02T15:04:06| :time} {{{$t :date}}}" - } - ], - "time": [ - { "src": "{:time}", "exp": "{:time}", "errors": [{ "type": "bad-input" }] }, - { - "src": "{horse :time}", - "exp": "{|horse|}", - "errors": [{ "type": "bad-input" }] - }, - { "src": "{|2006-01-02T15:04:06| :time}" }, - { - "src": "{|2006-01-02T15:04:06| :time style=medium}" - }, - { - "src": ".local $t = {|2006-01-02T15:04:06| :time style=medium} {{{$t :time}}}" - }, - { - "src": ".local $d = {|2006-01-02T15:04:06| :date} {{{$d :time}}}" - } - ], - "datetime": [ - { - "src": "{:datetime}", - "exp": "{:datetime}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "{$x :datetime}", - "exp": "{$x}", - "params": { "x": true }, - "errors": [{ "type": "bad-input" }] - }, - { - "src": "{horse :datetime}", - "exp": "{|horse|}", - "errors": [{ "name": "RangeError" }] - }, - { "src": "{|2006-01-02T15:04:06| :datetime}" }, - { - "src": "{|2006-01-02T15:04:06| :datetime year=numeric month=|2-digit|}" - }, - { - "src": "{|2006-01-02T15:04:06| :datetime dateStyle=long}" - }, - { - "src": "{|2006-01-02T15:04:06| :datetime timeStyle=medium}" - }, - { - "src": "{$dt :datetime}", - "params": { "dt": "2006-01-02T15:04:06" } - } - ], - "integer": [ - { "src": "hello {4.2 :integer}", "exp": "hello 4" }, - { "src": "hello {-4.20 :integer}", "exp": "hello -4" }, - { "src": "hello {0.42e+1 :integer}", "exp": "hello 4" }, - { - "src": ".match {$foo :integer} one {{one}} * {{other}}", - "params": { "foo": 1.2 }, - "exp": "one" - } - ], - "number": [ - { "src": "hello {4.2 :number}", "exp": "hello 4.2" }, - { "src": "hello {-4.20 :number}", "exp": "hello -4.2" }, - { "src": "hello {0.42e+1 :number}", "exp": "hello 4.2" }, - { - "src": "hello {foo :number}", - "exp": "hello {|foo|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "invalid number literal {.1 :number}", - "exp": "invalid number literal {|.1|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "invalid number literal {1. :number}", - "exp": "invalid number literal {|1.|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "invalid number literal {01 :number}", - "exp": "invalid number literal {|01|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "invalid number literal {|+1| :number}", - "exp": "invalid number literal {|+1|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "invalid number literal {0x1 :number}", - "exp": "invalid number literal {|0x1|}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "hello {:number}", - "exp": "hello {:number}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": "hello {4.2 :number minimumFractionDigits=2}", - "exp": "hello 4.20" - }, - { - "src": "hello {|4.2| :number minimumFractionDigits=|2|}", - "exp": "hello 4.20" - }, - { - "src": "hello {4.2 :number minimumFractionDigits=$foo}", - "params": { "foo": 2 }, - "exp": "hello 4.20" - }, - { - "src": "hello {|4.2| :number minimumFractionDigits=$foo}", - "params": { "foo": "2" }, - "exp": "hello 4.20" - }, - { - "src": ".local $foo = {$bar :number} {{bar {$foo}}}", - "params": { "bar": 4.2 }, - "exp": "bar 4.2" - }, - { - "src": ".local $foo = {$bar :number minimumFractionDigits=2} {{bar {$foo}}}", - "params": { "bar": 4.2 }, - "exp": "bar 4.20" - }, - { - "src": ".local $foo = {$bar :number minimumFractionDigits=foo} {{bar {$foo}}}", - "params": { "bar": 4.2 }, - "exp": "bar {$bar}", - "errors": [{ "type": "bad-option" }] - }, - { - "src": ".local $foo = {$bar :number} {{bar {$foo}}}", - "params": { "bar": "foo" }, - "exp": "bar {$bar}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": ".input {$foo :number} {{bar {$foo}}}", - "params": { "foo": 4.2 }, - "exp": "bar 4.2" - }, - { - "src": ".input {$foo :number minimumFractionDigits=2} {{bar {$foo}}}", - "params": { "foo": 4.2 }, - "exp": "bar 4.20" - }, - { - "src": ".input {$foo :number minimumFractionDigits=foo} {{bar {$foo}}}", - "params": { "foo": 4.2 }, - "exp": "bar {$foo}", - "errors": [{ "type": "bad-option" }] - }, - { - "src": ".input {$foo :number} {{bar {$foo}}}", - "params": { "foo": "foo" }, - "exp": "bar {$foo}", - "errors": [{ "type": "bad-input" }] - }, - { - "src": ".match {$foo :number} one {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".match {$foo :number} 1 {{=1}} one {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "=1" - }, - { - "src": ".match {$foo :number} one {{one}} 1 {{=1}} * {{other}}", - "params": { "foo": 1 }, - "exp": "=1" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": { "foo": 1, "bar": 1 }, - "exp": "one one" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": { "foo": 1, "bar": 2 }, - "exp": "one other" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": { "foo": 2, "bar": 2 }, - "exp": "other" - }, - { - "src": ".input {$foo :number} .match {$foo} one {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".local $foo = {$bar :number} .match {$foo} one {{one}} * {{other}}", - "params": { "bar": 1 }, - "exp": "one" - }, - { - "src": ".input {$foo :number} .local $bar = {$foo} .match {$bar} one {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".input {$bar :number} .match {$bar} one {{one}} * {{other}}", - "params": { "bar": 2 }, - "exp": "other" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": { "bar": 1 }, - "exp": "one" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": { "bar": 2 }, - "exp": "other" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": { "bar": 1 }, - "exp": "one" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": { "bar": 2 }, - "exp": "other" - }, - { - "src": ".input {$none} .match {$foo :number} one {{one}} * {{{$none}}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", - "params": { "foo": 2 }, - "exp": "{$none}", - "errors": [{ "type": "unresolved-var" }] - }, - { - "src": "{42 :number @foo @bar=13}", - "exp": "42", - "parts": [ - { "type": "number", "parts": [{ "type": "integer", "value": "42" }] } - ] - } - ], - "ordinal": [ - { - "src": ".match {$foo :ordinal} one {{st}} two {{nd}} few {{rd}} * {{th}}", - "params": { "foo": 1 }, - "exp": "th", - "errors": [{ "type": "missing-func" }, { "type": "not-selectable" }] - }, - { - "src": "hello {42 :ordinal}", - "exp": "hello {|42|}", - "errors": [{ "type": "missing-func" }] - } - ], - "plural": [ - { - "src": ".match {$foo :plural} one {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "other", - "errors": [{ "type": "missing-func" }, { "type": "not-selectable" }] - }, - { - "src": "hello {42 :plural}", - "exp": "hello {|42|}", - "errors": [{ "type": "missing-func" }] - } - ], - "string": [ - { - "src": ".match {$foo :string} |1| {{one}} * {{other}}", - "params": { "foo": "1" }, - "exp": "one" - }, - { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", - "params": { "foo": 1 }, - "exp": "one" - }, - { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", - "params": { "foo": null }, - "exp": "other" - }, - { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", - "exp": "other", - "errors": [{ "type": "unresolved-var" }] - } - ] -} diff --git a/test/test-functions.json.d.ts b/test/test-functions.json.d.ts deleted file mode 100644 index 423efb567..000000000 --- a/test/test-functions.json.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { TestMessage } from './test-core.json'; - -declare const data: Record; -export default data; diff --git a/test/tests/data-model-errors.json b/test/tests/data-model-errors.json new file mode 100644 index 000000000..c9be023c4 --- /dev/null +++ b/test/tests/data-model-errors.json @@ -0,0 +1,168 @@ +{ + "scenario": "Data model errors", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {$foo :x} * * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + { + "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + { + "src": ".match {:foo} 1 {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {:foo} other {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {:foo} {:bar} * 1 {{_}} 1 * {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".input {$foo} .input {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".input {$foo} .local $foo = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42} .input {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {:unknown} .local $foo = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar} .local $bar = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar} .local $bar = {$baz} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar :func} .local $bar = {$baz} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42 :func opt=$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42 :func opt=$bar} .local $bar = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": "bad {:placeholder option=x option=x}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + { + "src": "bad {:placeholder ns:option=x ns:option=y}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + } + ] +} diff --git a/test/tests/functions/date.json b/test/tests/functions/date.json new file mode 100644 index 000000000..472b46d86 --- /dev/null +++ b/test/tests/functions/date.json @@ -0,0 +1,43 @@ +{ + "scenario": "Date function", + "description": "The built-in formatter for dates.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": false + }, + "tests": [ + { + "src": "{:date}", + "exp": "{:date}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "{horse :date}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "operand-mismatch" + } + ] + }, + { + "src": "{|2006-01-02| :date}" + }, + { + "src": "{|2006-01-02T15:04:06| :date}" + }, + { + "src": "{|2006-01-02| :date style=long}" + }, + { + "src": ".local $d = {|2006-01-02| :date style=long} {{{$d :date}}}" + }, + { + "src": ".local $t = {|2006-01-02T15:04:06| :time} {{{$t :date}}}" + } + ] +} diff --git a/test/tests/functions/datetime.json b/test/tests/functions/datetime.json new file mode 100644 index 000000000..18e0cd974 --- /dev/null +++ b/test/tests/functions/datetime.json @@ -0,0 +1,65 @@ +{ + "scenario": "Datetime function", + "description": "The built-in formatter for datetimes.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": false + }, + "tests": [ + { + "src": "{:datetime}", + "exp": "{:datetime}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "{$x :datetime}", + "exp": "{$x}", + "params": [ + { + "name": "x", + "value": true + } + ], + "expErrors": [ + { + "type": "bad-input" + } + ] + }, + { + "src": "{horse :datetime}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "operand-mismatch" + } + ] + }, + { + "src": "{|2006-01-02T15:04:06| :datetime}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime year=numeric month=|2-digit|}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime dateStyle=long}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime timeStyle=medium}" + }, + { + "src": "{$dt :datetime}", + "params": [ + { + "type": "datetime", + "name": "dt", + "value": "2006-01-02T15:04:06" + } + ] + } + ] +} diff --git a/test/tests/functions/integer.json b/test/tests/functions/integer.json new file mode 100644 index 000000000..1c16f54c7 --- /dev/null +++ b/test/tests/functions/integer.json @@ -0,0 +1,31 @@ +{ + "scenario": "Integer function", + "description": "The built-in formatter for integers.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello {4.2 :integer}", + "exp": "hello 4" + }, + { + "src": "hello {-4.20 :integer}", + "exp": "hello -4" + }, + { + "src": "hello {0.42e+1 :integer}", + "exp": "hello 4" + }, + { + "src": ".match {$foo :integer} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1.2 + } + ], + "exp": "one" + } + ] +} diff --git a/test/tests/functions/number.json b/test/tests/functions/number.json new file mode 100644 index 000000000..515600c45 --- /dev/null +++ b/test/tests/functions/number.json @@ -0,0 +1,415 @@ +{ + "scenario": "Number function", + "description": "The built-in formatter for numbers.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello {4.2 :number}", + "exp": "hello 4.2" + }, + { + "src": "hello {-4.20 :number}", + "exp": "hello -4.2" + }, + { + "src": "hello {0.42e+1 :number}", + "exp": "hello 4.2" + }, + { + "src": "hello {foo :number}", + "exp": "hello {|foo|}", + "expErrors": [ + { + "type": "operand-mismatch" + } + ] + }, + { + "src": "invalid number literal {.1 :number}", + "exp": "invalid number literal {|.1|}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "invalid number literal {1. :number}", + "exp": "invalid number literal {|1.|}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "invalid number literal {01 :number}", + "exp": "invalid number literal {|01|}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "invalid number literal {|+1| :number}", + "exp": "invalid number literal {|+1|}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "invalid number literal {0x1 :number}", + "exp": "invalid number literal {|0x1|}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "hello {:number}", + "exp": "hello {:number}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "hello {4.2 :number minimumFractionDigits=2}", + "exp": "hello 4.20" + }, + { + "src": "hello {|4.2| :number minimumFractionDigits=|2|}", + "exp": "hello 4.20" + }, + { + "src": "hello {4.2 :number minimumFractionDigits=$foo}", + "params": [ + { + "name": "foo", + "value": 2 + } + ], + "exp": "hello 4.20" + }, + { + "src": "hello {|4.2| :number minimumFractionDigits=$foo}", + "params": [ + { + "name": "foo", + "value": "2" + } + ], + "exp": "hello 4.20" + }, + { + "src": ".local $foo = {$bar :number} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar 4.2" + }, + { + "src": ".local $foo = {$bar :number minimumFractionDigits=2} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar 4.20" + }, + { + "src": ".local $foo = {$bar :number minimumFractionDigits=foo} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar {$bar}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": ".local $foo = {$bar :number} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": "foo" + } + ], + "exp": "bar {$bar}", + "expErrors": [ + { + "type": "bad-input" + } + ] + }, + { + "src": ".input {$foo :number} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar 4.2" + }, + { + "src": ".input {$foo :number minimumFractionDigits=2} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar 4.20" + }, + { + "src": ".input {$foo :number minimumFractionDigits=foo} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar {$foo}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": ".input {$foo :number} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar {$foo}", + "expErrors": [ + { + "type": "bad-input" + } + ] + }, + { + "src": ".match {$foo :number} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".match {$foo :number} 1 {{=1}} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "=1" + }, + { + "src": ".match {$foo :number} one {{one}} 1 {{=1}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "=1" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + }, + { + "name": "bar", + "value": 1 + } + ], + "exp": "one one" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + }, + { + "name": "bar", + "value": 2 + } + ], + "exp": "one other" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 2 + }, + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$foo :number} .match {$foo} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $foo = {$bar :number} .match {$foo} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$foo :number} .local $bar = {$foo} .match {$bar} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$bar :number} .match {$bar} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$none} .match {$foo :number} one {{one}} * {{{$none}}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "params": [ + { + "name": "foo", + "value": 2 + } + ], + "exp": "{$none}", + "expErrors": [ + { + "type": "unresolved-variable" + } + ] + }, + { + "src": "{42 :number @foo @bar=13}", + "exp": "42", + "expParts": [ + { + "type": "number", + "source": "|42|", + "parts": [ + { + "type": "integer", + "value": "42" + } + ] + } + ] + } + ] +} diff --git a/test/tests/functions/string.json b/test/tests/functions/string.json new file mode 100644 index 000000000..e1782f1a0 --- /dev/null +++ b/test/tests/functions/string.json @@ -0,0 +1,48 @@ +{ + "scenario": "String function", + "description": "The built-in formatter for strings.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {$foo :string} |1| {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": "1" + } + ], + "exp": "one" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": null + } + ], + "exp": "other" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "exp": "other", + "expErrors": [ + { + "type": "unresolved-variable" + } + ] + } + ] +} diff --git a/test/tests/functions/time.json b/test/tests/functions/time.json new file mode 100644 index 000000000..4c5dd22bb --- /dev/null +++ b/test/tests/functions/time.json @@ -0,0 +1,40 @@ +{ + "scenario": "Time function", + "description": "The built-in formatter for times.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": false + }, + "tests": [ + { + "src": "{:time}", + "exp": "{:time}", + "expErrors": [ + { + "type": "invalid-expression" + } + ] + }, + { + "src": "{horse :time}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "operand-mismatch" + } + ] + }, + { + "src": "{|2006-01-02T15:04:06| :time}" + }, + { + "src": "{|2006-01-02T15:04:06| :time style=medium}" + }, + { + "src": ".local $t = {|2006-01-02T15:04:06| :time style=medium} {{{$t :time}}}" + }, + { + "src": ".local $d = {|2006-01-02T15:04:06| :date} {{{$d :time}}}" + } + ] +} diff --git a/test/tests/syntax-errors.json b/test/tests/syntax-errors.json new file mode 100644 index 000000000..752d67b8a --- /dev/null +++ b/test/tests/syntax-errors.json @@ -0,0 +1,176 @@ +{ + "scenario": "Syntax errors", + "description": "Strings that produce syntax errors when parsed.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { + "src": "." + }, + { + "src": "{" + }, + { + "src": "}" + }, + { + "src": "{}" + }, + { + "src": "{{" + }, + { + "src": "{{}" + }, + { + "src": "{{}}}" + }, + { + "src": "{|foo| #markup}" + }, + { + "src": "{{missing end brace}" + }, + { + "src": "{{missing end braces" + }, + { + "src": "{{missing end {$braces" + }, + { + "src": "{{extra}} content" + }, + { + "src": "empty { } placeholder" + }, + { + "src": "missing space {42:func}" + }, + { + "src": "missing space {|foo|:func}" + }, + { + "src": "missing space {|foo|@bar}" + }, + { + "src": "missing space {:func@bar}" + }, + { + "src": "missing space {:func @bar@baz}" + }, + { + "src": "missing space {:func @bar=42@baz}" + }, + { + "src": "missing space {+reserved@bar}" + }, + { + "src": "missing space {&private@bar}" + }, + { + "src": "bad {:} placeholder" + }, + { + "src": "bad {\\u0000placeholder}" + }, + { + "src": "no-equal {|42| :number minimumFractionDigits 2}" + }, + { + "src": "bad {:placeholder option=}" + }, + { + "src": "bad {:placeholder option value}" + }, + { + "src": "bad {:placeholder option:value}" + }, + { + "src": "bad {:placeholder option}" + }, + { + "src": "bad {:placeholder:}" + }, + { + "src": "bad {::placeholder}" + }, + { + "src": "bad {:placeholder::foo}" + }, + { + "src": "bad {:placeholder option:=x}" + }, + { + "src": "bad {:placeholder :option=x}" + }, + { + "src": "bad {:placeholder option::x=y}" + }, + { + "src": "bad {$placeholder option}" + }, + { + "src": "bad {:placeholder @attribute=}" + }, + { + "src": "bad {:placeholder @attribute=@foo}" + }, + { + "src": "no {placeholder end" + }, + { + "src": "no {$placeholder end" + }, + { + "src": "no {:placeholder end" + }, + { + "src": "no {|placeholder| end" + }, + { + "src": "no {|literal} end" + }, + { + "src": "no {|literal or placeholder end" + }, + { + "src": ".local bar = {|foo|} {{_}}" + }, + { + "src": ".local #bar = {|foo|} {{_}}" + }, + { + "src": ".local $bar {|foo|} {{_}}" + }, + { + "src": ".local $bar = |foo| {{_}}" + }, + { + "src": ".match {#foo} * {{foo}}" + }, + { + "src": ".match {} * {{foo}}" + }, + { + "src": ".match {|foo| :x} {|bar| :x} ** {{foo}}" + }, + { + "src": ".match * {{foo}}" + }, + { + "src": ".match {|x| :x} * foo" + }, + { + "src": ".match {|x| :x} * {{foo}} extra" + }, + { + "src": ".match |x| * {{foo}}" + } + ] +} diff --git a/test/tests/syntax.json b/test/tests/syntax.json new file mode 100644 index 000000000..7206e9f06 --- /dev/null +++ b/test/tests/syntax.json @@ -0,0 +1,414 @@ +{ + "scenario": "Syntax", + "description": "Test cases that do not depend on any registry definitions.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello", + "exp": "hello" + }, + { + "src": "hello {world}", + "exp": "hello world" + }, + { + "src": "hello { world\t\n}", + "exp": "hello world", + "expCleanSrc": "hello {world}" + }, + { + "src": "hello {\u3000world\r}", + "exp": "hello world", + "expCleanSrc": "hello {world}" + }, + { + "src": "hello {|world|}", + "exp": "hello world" + }, + { + "src": "hello {||}", + "exp": "hello " + }, + { + "src": "hello {$place}", + "params": [ + { + "name": "place", + "value": "world" + } + ], + "exp": "hello world" + }, + { + "src": "hello {$place-.}", + "params": [ + { + "name": "place-.", + "value": "world" + } + ], + "exp": "hello world" + }, + { + "src": "hello {$place}", + "expErrors": [ + { + "type": "unresolved-variable" + } + ], + "exp": "hello {$place}" + }, + { + "src": "{$one} and {$two}", + "params": [ + { + "name": "one", + "value": 1.3 + }, + { + "name": "two", + "value": 4.2 + } + ], + "exp": "1.3 and 4.2" + }, + { + "src": "{$one} et {$two}", + "locale": "fr", + "params": [ + { + "name": "one", + "value": 1.3 + }, + { + "name": "two", + "value": 4.2 + } + ], + "exp": "1,3 et 4,2" + }, + { + "src": ".local $foo = {bar} {{bar {$foo}}}", + "exp": "bar bar" + }, + { + "src": ".local $foo = {|bar|} {{bar {$foo}}}", + "exp": "bar bar" + }, + { + "src": ".local $foo = {|bar|} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar bar" + }, + { + "src": ".local $foo = {$bar} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "baz", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".input {$foo} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".input {$foo} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "baz", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $x = {42} .local $y = {$x} {{{$x} {$y}}}", + "exp": "42 42" + }, + { + "src": "{#tag}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag" + } + ] + }, + { + "src": "{#tag}content", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag" + }, + { + "type": "literal", + "value": "content" + } + ] + }, + { + "src": "{#ns:tag}content{/ns:tag}", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "ns:tag" + }, + { + "type": "literal", + "value": "content" + }, + { + "type": "markup", + "kind": "close", + "name": "ns:tag" + } + ] + }, + { + "src": "{/tag}content", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "close", + "name": "tag" + }, + { + "type": "literal", + "value": "content" + } + ] + }, + { + "src": "{#tag foo=bar}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "src": "{#tag foo=bar/}", + "expCleanSrc": "{#tag foo=bar /}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "standalone", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "src": "{#tag a:foo=|foo| b:bar=$bar}", + "params": [ + { + "name": "bar", + "value": "b a r" + } + ], + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag", + "options": { + "a:foo": "foo", + "b:bar": "b a r" + } + } + ] + }, + { + "src": "{/tag foo=bar}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "close", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "src": "{42 @foo @bar=13}", + "exp": "42", + "expParts": [ + { + "type": "literal", + "value": "42" + } + ] + }, + { + "src": "{42 @foo=$bar}", + "exp": "42", + "expParts": [ + { + "type": "literal", + "value": "42" + } + ] + }, + { + "src": "foo {+reserved}", + "exp": "foo {+}", + "expParts": [ + { + "type": "literal", + "value": "foo " + }, + { + "type": "fallback", + "source": "+" + } + ], + "expErrors": [ + { + "type": "unsupported-expression" + } + ] + }, + { + "src": "foo {&private}", + "exp": "foo {&}", + "expParts": [ + { + "type": "literal", + "value": "foo " + }, + { + "type": "fallback", + "source": "&" + } + ], + "expErrors": [ + { + "type": "unsupported-expression" + } + ] + }, + { + "src": "foo {?reserved @a @b=$c}", + "exp": "foo {?}", + "expParts": [ + { + "type": "literal", + "value": "foo " + }, + { + "type": "fallback", + "source": "?" + } + ], + "expErrors": [ + { + "type": "unsupported-expression" + } + ] + }, + { + "src": ".foo {42} {{bar}}", + "exp": "bar", + "expParts": [ + { + "type": "literal", + "value": "bar" + } + ], + "expErrors": [ + { + "type": "unsupported-statement" + } + ] + }, + { + "src": ".foo{42}{{bar}}", + "expCleanSrc": ".foo {42} {{bar}}", + "exp": "bar", + "expParts": [ + { + "type": "literal", + "value": "bar" + } + ], + "expErrors": [ + { + "type": "unsupported-statement" + } + ] + }, + { + "src": ".foo |}lit{| {42}{{bar}}", + "expCleanSrc": ".foo |}lit{| {42} {{bar}}", + "exp": "bar", + "expParts": [ + { + "type": "literal", + "value": "bar" + } + ], + "expErrors": [ + { + "type": "unsupported-statement" + } + ] + } + ] +}