diff --git a/.mapping.json b/.mapping.json index 468f8f722..1230c5646 100644 --- a/.mapping.json +++ b/.mapping.json @@ -845,6 +845,8 @@ "client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonParserTest.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonParserTest.kt", "client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonPrinterTest.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonPrinterTest.kt", "client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonTopologicalSortingTest.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/parser/JsonTopologicalSortingTest.kt", + "client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingMultiplatformTest.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingMultiplatformTest.kt", + "client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingTestCase.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingTestCase.kt", "client/android/div-data/src/test/java/com/yandex/div/internal/util/JsonUtilsTest.kt":"divkit/public/client/android/div-data/src/test/java/com/yandex/div/internal/util/JsonUtilsTest.kt", "client/android/div-data/src/test/resources/com/yandex/div/core/json/cyclic-dependency-composition.json":"divkit/public/client/android/div-data/src/test/resources/com/yandex/div/core/json/cyclic-dependency-composition.json", "client/android/div-data/src/test/resources/com/yandex/div/core/json/cyclic-dependency-inheritance.json":"divkit/public/client/android/div-data/src/test/resources/com/yandex/div/core/json/cyclic-dependency-inheritance.json", diff --git a/client/android/div-data/build.gradle.kts b/client/android/div-data/build.gradle.kts index 9d62f5369..2dc36ce75 100644 --- a/client/android/div-data/build.gradle.kts +++ b/client/android/div-data/build.gradle.kts @@ -22,6 +22,9 @@ dependencies { api(libs.androidx.core) + testImplementation(project(":test-utils")) + + testImplementation(libs.json) testImplementation(libs.kotlin.reflect) } diff --git a/client/android/div-data/src/main/java/com/yandex/div/internal/parser/JsonPropertyParser.java b/client/android/div-data/src/main/java/com/yandex/div/internal/parser/JsonPropertyParser.java index 8e03777e9..fb1c32bce 100644 --- a/client/android/div-data/src/main/java/com/yandex/div/internal/parser/JsonPropertyParser.java +++ b/client/android/div-data/src/main/java/com/yandex/div/internal/parser/JsonPropertyParser.java @@ -774,7 +774,7 @@ public class JsonPropertyParser { @Nullable final List list, @NonNull final Function1 converter ) { - if (list != null && !list.isEmpty()) { + if (list != null) { int length = list.size(); JSONArray array = new JSONArray(); for (int i = 0; i < length; i++) { @@ -796,7 +796,7 @@ public class JsonPropertyParser { @Nullable final List list, @NonNull final Lazy> serializer ) { - if (list != null && !list.isEmpty()) { + if (list != null) { int length = list.size(); JSONArray array = new JSONArray(); diff --git a/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingMultiplatformTest.kt b/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingMultiplatformTest.kt new file mode 100644 index 000000000..e03dcae6c --- /dev/null +++ b/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingMultiplatformTest.kt @@ -0,0 +1,103 @@ +package com.yandex.div.internal.parser + +import com.yandex.div.data.DivParsingEnvironment +import com.yandex.div.internal.util.forEach +import com.yandex.div.json.ParsingErrorLogger +import com.yandex.div.test.crossplatform.ParsingResult +import com.yandex.div.test.crossplatform.ParsingUtils.parseFiles +import com.yandex.div.test.crossplatform.isForAndroid +import com.yandex.div2.DivData +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class ParsingMultiplatformTest(testCaseParsingResult: ParsingResult) { + + private val testCase = testCaseParsingResult.getOrThrow() + private val errors = mutableListOf() + + @Test + fun run() { + val logger = ParsingErrorLogger { errors += it } + val env = DivParsingEnvironment(logger) + + val divData = runCatching { + testCase.templates?.let { env.parseTemplates(it) } + DivData(env, testCase.card) + }.getOrNull() + + assertCardEquals(testCase.expectedResult.card, divData?.writeToJSON()) + Assert.assertEquals("Unexpected error count:", testCase.expectedResult.errorCount, errors.size) + } + + private fun assertCardEquals(expected: JSONObject?, actual: JSONObject?) { + expected ?: return Assert.assertNull(actual) + actual ?: return Assert.fail( + "Failed to parse DivData. Errors: ${errors.joinToString(", ") { it.message ?: "" }}" + ) + + runCatching { + expected.compareValues("", actual) + }.onFailure { + Assert.fail(it.message) + } + } + + private fun JSONObject.compareValues(parentPath: String, actual: JSONObject): Boolean { + forEach { key, expectedValue: Any? -> + val actualValue = actual.opt(key) + val path = parentPath.takeIf { it.isNotEmpty() }?.let { "$it/$key" } ?: key + if (!expectedValue.compare(path, actualValue)) { + throwError(path, expectedValue, actualValue) + } + } + return true + } + + private fun Any?.compare(path: String, actual: Any?): Boolean { + return when (this) { + JSONObject.NULL -> actual == null || actual == this + is JSONObject -> (actual as? JSONObject)?.let { compareValues(path, it) } ?: false + is JSONArray -> (actual as? JSONArray)?.let { compareValues(path, it)} ?: false + is Int -> actual == this || (actual as? Long)?.toInt() == this + else -> actual == this + } + } + + private fun JSONArray.compareValues(parentPath: String, actual: JSONArray): Boolean { + forEach { i, expectedValue: Any? -> + val actualValue = actual.opt(i) + val path = parentPath.takeIf { it.isNotEmpty() }?.let { "$it/$i" } ?: "$i" + if (!expectedValue.compare(path, actualValue)) { + throwError(path, expectedValue, actualValue) + } + } + return true + } + + private fun throwError(path: String, expected: Any?, actual: Any?): Nothing = + throw Exception("DivData comparison failed for path '$path', expected='$expected', actual='$actual'") + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun cases(): List> { + return parseFiles("parsing_test_data") { file, jsonString -> + val json = JSONObject(jsonString) + if (!json.isForAndroid) return@parseFiles emptyList() + + val result = try { + val testCase = ParsingTestCase.from(file.name, json) + ParsingResult.Success(testCase) + } catch (e: Exception) { + ParsingResult.Error(fileName = file.name, error = e) + } + listOf(result) + } + } + } +} diff --git a/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingTestCase.kt b/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingTestCase.kt new file mode 100644 index 000000000..40c10c469 --- /dev/null +++ b/client/android/div-data/src/test/java/com/yandex/div/internal/parser/ParsingTestCase.kt @@ -0,0 +1,35 @@ +package com.yandex.div.internal.parser + +import org.json.JSONException +import org.json.JSONObject + +class ParsingTestCase( + val name: String, + val templates: JSONObject?, + val card: JSONObject, + val expectedResult: ExpectedResult, +) { + + class ExpectedResult( + val errorCount: Int?, + val card: JSONObject?, + ) + + override fun toString() = name + + companion object { + @Throws(JSONException::class) + fun from(name: String, json: JSONObject): ParsingTestCase { + val expectedResult = json.getJSONObject("expected") + return ParsingTestCase( + name = name, + templates = json.optJSONObject("templates"), + card = json.getJSONObject("card"), + expectedResult = ExpectedResult( + errorCount = expectedResult.optInt("error_count"), + card = expectedResult.optJSONObject("card"), + ), + ) + } + } +} diff --git a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCase.kt b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCase.kt index da84b7a70..79c86195d 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCase.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCase.kt @@ -8,7 +8,6 @@ data class ExpressionTestCase( val expression: String, val variables: List, val functions: List, - val platform: List, val expectedType: String, val expectedValue: Any, val expectedWarnings: List, diff --git a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt index 741ab2b4c..2b74eefa3 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt @@ -10,7 +10,6 @@ import com.yandex.div.json.ParsingErrorLogger import com.yandex.div.test.crossplatform.ParsingResult import com.yandex.div.test.crossplatform.isForAndroid import com.yandex.div.test.crossplatform.parseDateTime -import com.yandex.div.test.crossplatform.platforms import com.yandex.div.test.crossplatform.toObjectList import com.yandex.div2.DivData import com.yandex.div2.DivEvaluableType @@ -64,7 +63,6 @@ object ExpressionTestCaseUtils { json.getString(CASE_EXPRESSION_VALUE_FIELD), json.optJSONArray(CASE_VARIABLES_FIELD).toObjectList(), json.optJSONArray(CASE_FUNCTIONS_FIELD).toObjectList(), - json.platforms, json.getJSONObject(CASE_EXPECTED_VALUE_FIELD).type, json.getJSONObject(CASE_EXPECTED_VALUE_FIELD).getValue(), json.optJSONArray(CASE_EXPECTED_WARNINGS_FIELD)?.map { it as String } ?: emptyList(), diff --git a/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt b/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt index 65d77fb52..ecb267ee2 100644 --- a/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt +++ b/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt @@ -55,11 +55,11 @@ object ParsingUtils { } } -val JSONObject.platforms: List - get() = getJSONArray("platforms").map { it as String } +val JSONObject.platforms: List? + get() = optJSONArray("platforms")?.map { it as String } val JSONObject.isForAndroid: Boolean - get() = platforms.contains("android") + get() = platforms?.contains("android") ?: true fun JSONArray?.toObjectList(): List { if (this == null) { diff --git a/test_data/parsing_test_data/arrays/invalid_items.json b/test_data/parsing_test_data/arrays/invalid_items.json index c7f0aef3a..789271484 100644 --- a/test_data/parsing_test_data/arrays/invalid_items.json +++ b/test_data/parsing_test_data/arrays/invalid_items.json @@ -1,5 +1,11 @@ { "description": "Invalid item in array (transition_triggers) is ignored", + "platforms": [ + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/integer_in_boolean_int_property.json b/test_data/parsing_test_data/casting/integer_in_boolean_int_property.json index 8b038c4bf..f7039cd9d 100644 --- a/test_data/parsing_test_data/casting/integer_in_boolean_int_property.json +++ b/test_data/parsing_test_data/casting/integer_in_boolean_int_property.json @@ -1,5 +1,12 @@ { "description": "Integer value in boolean_int property (div-text.auto_ellipsize)", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/integer_in_string_property.json b/test_data/parsing_test_data/casting/integer_in_string_property.json index 86ed43b63..757c4fb35 100644 --- a/test_data/parsing_test_data/casting/integer_in_string_property.json +++ b/test_data/parsing_test_data/casting/integer_in_string_property.json @@ -1,5 +1,12 @@ { "description": "Integer value in string property is not valid (div-text.text)", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/invalid_integer_in_boolean_int_property.json b/test_data/parsing_test_data/casting/invalid_integer_in_boolean_int_property.json index d3379e896..111c7d9cb 100644 --- a/test_data/parsing_test_data/casting/invalid_integer_in_boolean_int_property.json +++ b/test_data/parsing_test_data/casting/invalid_integer_in_boolean_int_property.json @@ -1,5 +1,11 @@ { "description": "Invalid integer value in boolean_int property (div-text.auto_ellipsize)", + "platforms": [ + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/invalid_number_in_boolean_int_property.json b/test_data/parsing_test_data/casting/invalid_number_in_boolean_int_property.json index 67196557a..3c0dbf02d 100644 --- a/test_data/parsing_test_data/casting/invalid_number_in_boolean_int_property.json +++ b/test_data/parsing_test_data/casting/invalid_number_in_boolean_int_property.json @@ -1,5 +1,11 @@ { "description": "Invalid number value in boolean_int property (div-text.auto_ellipsize)", + "platforms": [ + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/number_in_boolean_int_property.json b/test_data/parsing_test_data/casting/number_in_boolean_int_property.json index 71e8d725b..790440ed2 100644 --- a/test_data/parsing_test_data/casting/number_in_boolean_int_property.json +++ b/test_data/parsing_test_data/casting/number_in_boolean_int_property.json @@ -1,5 +1,12 @@ { "description": "Number value in boolean_int property (div-text.auto_ellipsize)", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/number_in_integer_property.json b/test_data/parsing_test_data/casting/number_in_integer_property.json index 6f1fb158e..3dea4bed4 100644 --- a/test_data/parsing_test_data/casting/number_in_integer_property.json +++ b/test_data/parsing_test_data/casting/number_in_integer_property.json @@ -1,5 +1,12 @@ { "description": "Number value in integer property (div-text.font_size)", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/number_in_string_property.json b/test_data/parsing_test_data/casting/number_in_string_property.json index 1cec435f4..60ab51fbd 100644 --- a/test_data/parsing_test_data/casting/number_in_string_property.json +++ b/test_data/parsing_test_data/casting/number_in_string_property.json @@ -1,5 +1,12 @@ { "description": "Number value in string property is not valid (div-text.text)", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/casting/string_in_boolean_int_property.json b/test_data/parsing_test_data/casting/string_in_boolean_int_property.json index 8f1b10b5d..060182878 100644 --- a/test_data/parsing_test_data/casting/string_in_boolean_int_property.json +++ b/test_data/parsing_test_data/casting/string_in_boolean_int_property.json @@ -1,5 +1,11 @@ { "description": "String value in boolean_int property (div-text.auto_ellipsize)", + "platforms": [ + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/expressions.json b/test_data/parsing_test_data/expressions.json index 050efac14..0511f3687 100644 --- a/test_data/parsing_test_data/expressions.json +++ b/test_data/parsing_test_data/expressions.json @@ -1,5 +1,12 @@ { "description": "All properties with expressions are parsed correctly", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ @@ -14,8 +21,7 @@ "text_color": "@{text_color}", "actions": [ { - "log_id": "test_action", - "params": "@{action_params}" + "log_id": "@{action_log_id}" } ] } @@ -37,8 +43,7 @@ "text_color": "@{text_color}", "actions": [ { - "log_id": "test_action", - "params": "@{action_params}" + "log_id": "@{action_log_id}" } ] } diff --git a/test_data/parsing_test_data/invalid_value_replaced_with_default_value.json b/test_data/parsing_test_data/invalid_value_replaced_with_default_value.json index 9e47940e4..4612e9c50 100644 --- a/test_data/parsing_test_data/invalid_value_replaced_with_default_value.json +++ b/test_data/parsing_test_data/invalid_value_replaced_with_default_value.json @@ -1,5 +1,12 @@ { "description": "Invalid value replaced with default value", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/required_property_value_is_missing.json b/test_data/parsing_test_data/required_property_value_is_missing.json index f978ed25e..3fcc9f5ac 100644 --- a/test_data/parsing_test_data/required_property_value_is_missing.json +++ b/test_data/parsing_test_data/required_property_value_is_missing.json @@ -1,5 +1,12 @@ { "description": "Object with missing required property (div-action.log_id) is ignored", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/simple_values.json b/test_data/parsing_test_data/simple_values.json index a117297b5..eacc828a5 100644 --- a/test_data/parsing_test_data/simple_values.json +++ b/test_data/parsing_test_data/simple_values.json @@ -1,5 +1,12 @@ { - "description": "All values are parsed corectly", + "description": "All values are parsed correctly", + "platforms": [ + "android", + "ios" + ], + "unsupported_platforms": { + "web": "There is no \"parsing\" process on the web" + }, "card": { "log_id": "test", "states": [ @@ -12,15 +19,15 @@ { "type": "gradient", "colors": [ - "#112233", - "#332211" + "#FF112233", + "#FF332211" ] } ], "font_weight": "bold", "font_size": 20, "text": "Hello!", - "text_color": "#AABBCC", + "text_color": "#FFAABBCC", "transition_triggers": [ "data_change", "state_change" @@ -42,15 +49,15 @@ { "type": "gradient", "colors": [ - "#112233", - "#332211" + "#FF112233", + "#FF332211" ] } ], "font_weight": "bold", "font_size": 20, "text": "Hello!", - "text_color": "#AABBCC", + "text_color": "#FFAABBCC", "transition_triggers": [ "data_change", "state_change" diff --git a/test_data/parsing_test_data/templates/derived_template_preserves_parent_references.json b/test_data/parsing_test_data/templates/derived_template_preserves_parent_references.json index 02c26dff5..bb983741d 100644 --- a/test_data/parsing_test_data/templates/derived_template_preserves_parent_references.json +++ b/test_data/parsing_test_data/templates/derived_template_preserves_parent_references.json @@ -1,5 +1,9 @@ { "description": "A derived template inherits a parent template whose body contains reference definitions inside nested structures. Verifies that inherited references from the parent and the derived template's own references all resolve against the usage's reference values.", + "platforms": [ + "ios", + "web" + ], "templates": { "base_text": { "type": "text", diff --git a/test_data/parsing_test_data/templates/isolation_usage_reference_inside_instance_field.json b/test_data/parsing_test_data/templates/isolation_usage_reference_inside_instance_field.json index ff30e14a0..0f213e708 100644 --- a/test_data/parsing_test_data/templates/isolation_usage_reference_inside_instance_field.json +++ b/test_data/parsing_test_data/templates/isolation_usage_reference_inside_instance_field.json @@ -27,14 +27,14 @@ "state_id": 0, "div": { "type": "root", - "background_color": "#F00", + "background_color": "#FFFF0000", "items": [ { "type": "container", "items": [ { "type": "button", - "background_color": "#0f0", + "background_color": "#FF00FF00", "text": "SAMPLE" } ] @@ -55,7 +55,7 @@ "background": [ { "type": "solid", - "color": "#F00" + "color": "#FFFF0000" } ], "items": [ @@ -67,7 +67,7 @@ "background": [ { "type": "solid", - "color": "#0f0" + "color": "#FF00FF00" } ], "text": "SAMPLE" diff --git a/test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json b/test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json index d2593263d..c2ccf1212 100644 --- a/test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json +++ b/test_data/parsing_test_data/templates/reference_cascade_through_nested_usage.json @@ -1,5 +1,9 @@ { "description": "A template usage inside another template's body declares a reference definition whose source name matches a field on the card instance. Verifies that the reference value on the outer instance cascades through two levels of template usage to drive the inner template's field.", + "platforms": [ + "ios", + "web" + ], "templates": { "inner_template": { "type": "image", diff --git a/test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json b/test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json index 7828e3dfa..7927b8914 100644 --- a/test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json +++ b/test_data/parsing_test_data/templates/reference_in_plain_nested_dict.json @@ -1,5 +1,9 @@ { "description": "A reference definition lives inside a plain (non-template) nested dict embedded in a template's body — an element of an 'images' array. Verifies the reference still resolves against the outer instance despite the intermediate plain-dict layers.", + "platforms": [ + "ios", + "web" + ], "card": { "log_id": "test", "states": [ diff --git a/test_data/parsing_test_data/templates/reference_source_reused_at_multiple_depths.json b/test_data/parsing_test_data/templates/reference_source_reused_at_multiple_depths.json index 29f19a3f2..301b15df1 100644 --- a/test_data/parsing_test_data/templates/reference_source_reused_at_multiple_depths.json +++ b/test_data/parsing_test_data/templates/reference_source_reused_at_multiple_depths.json @@ -1,5 +1,9 @@ { "description": "A single template definition reuses the same reference source name ('content') at two depths: a top-level $text and a nested $log_id inside actions[]. Verifies both sites receive the same value from the usage's reference value.", + "platforms": [ + "ios", + "web" + ], "templates": { "action_text": { "type": "text", diff --git a/test_data/parsing_test_data/templates/reference_unresolved_for_optional_field.json b/test_data/parsing_test_data/templates/reference_unresolved_for_optional_field.json index 3b7d9bd51..7e3d8f4b7 100644 --- a/test_data/parsing_test_data/templates/reference_unresolved_for_optional_field.json +++ b/test_data/parsing_test_data/templates/reference_unresolved_for_optional_field.json @@ -1,5 +1,9 @@ { "description": "A reference definition points to a source name the usage does not provide, for an optional field. Verifies an unresolved reference on an optional field is silently ignored.", + "platforms": [ + "ios", + "web" + ], "templates": { "styled_text": { "type": "text", diff --git a/test_data/parsing_test_data/templates/references_at_multiple_depths.json b/test_data/parsing_test_data/templates/references_at_multiple_depths.json index aa9305b3e..fb440dcfe 100644 --- a/test_data/parsing_test_data/templates/references_at_multiple_depths.json +++ b/test_data/parsing_test_data/templates/references_at_multiple_depths.json @@ -1,5 +1,9 @@ { "description": "A template definition declares reference definitions at multiple depths: top-level ($text), inside a nested object ($top in paddings), and inside an array element ($url in actions[]). Verifies all references resolve against the usage's reference values.", + "platforms": [ + "ios", + "web" + ], "templates": { "title": { "type": "text",