{
  "schema": "https://bars.commonsware.com/conformance/schema/render-fixture.schema.json",
  "version": "0.1.0",
  "cases": [
    {
      "name": "each-array-iterates-in-order",
      "description": "A {{#each X }} block over a JsonArray iterates elements in order with {{this}}.",
      "template": "{{#each items }}({{ this }}){{/each}}",
      "context": {
        "items": [
          1,
          2,
          3
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "(1)(2)(3)"
      }
    },
    {
      "name": "each-array-of-objects-pushes-object-context",
      "description": "Iterating an array of objects lets the body resolve fields on each element.",
      "template": "{{#each items }}<{{ name }}>{{/each}}",
      "context": {
        "items": [
          {
            "name": "a"
          },
          {
            "name": "b"
          }
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "<a><b>"
      }
    },
    {
      "name": "each-object-iterates-in-insertion-key-order",
      "description": "A {{#each X }} block over a JsonObject iterates its entries in insertion order, pushing each value.",
      "template": "{{#each scores }}[{{ this }}]{{/each}}",
      "context": {
        "scores": {
          "c": 3,
          "a": 1,
          "b": 2
        }
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "[3][1][2]"
      }
    },
    {
      "name": "each-empty-array-no-else-emits-nothing",
      "description": "Empty array, no else arm: emits nothing.",
      "template": "before {{#each items }}({{ this }}){{/each}} after",
      "context": {
        "items": []
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "before  after"
      }
    },
    {
      "name": "each-empty-array-with-else-renders-else",
      "description": "Empty array, else arm present: renders the else arm.",
      "template": "{{#each items }}({{ this }}){{else}}none{{/each}}",
      "context": {
        "items": []
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "none"
      }
    },
    {
      "name": "each-empty-object-with-else-renders-else",
      "description": "Empty object, else arm present: renders the else arm.",
      "template": "{{#each scores }}{{ this }}{{else}}empty{{/each}}",
      "context": {
        "scores": {}
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "empty"
      }
    },
    {
      "name": "each-missing-key-no-else-emits-nothing",
      "description": "Identifier resolves to nothing (missing key), no else: emits nothing.",
      "template": "[{{#each absent }}x{{/each}}]",
      "context": {
        "items": [
          1,
          2
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "[]"
      }
    },
    {
      "name": "each-missing-key-with-else-renders-else",
      "description": "Identifier resolves to nothing (missing key), else arm present: renders the else arm.",
      "template": "{{#each absent }}x{{else}}fallback{{/each}}",
      "context": {
        "items": [
          1,
          2
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "fallback"
      }
    },
    {
      "name": "each-primitive-no-else-emits-nothing",
      "description": "Identifier resolves to a JsonPrimitive, no else: emits nothing.",
      "template": "[{{#each name }}x{{/each}}]",
      "context": {
        "name": "Alice"
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "[]"
      }
    },
    {
      "name": "each-primitive-with-else-renders-else",
      "description": "Identifier resolves to a JsonPrimitive, else arm present: renders the else arm.",
      "template": "{{#each name }}x{{else}}not iterable{{/each}}",
      "context": {
        "name": "Alice"
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "not iterable"
      }
    },
    {
      "name": "each-root-context-as-object-iterates-values",
      "description": "{{#each this}} with no nesting iterates the root context object's entries in insertion order, pushing each value.",
      "template": "{{#each this }}[{{ this }}]{{/each}}",
      "context": {
        "c": 3,
        "a": 1,
        "b": 2
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "[3][1][2]"
      }
    },
    {
      "name": "each-nested-blocks",
      "description": "Each blocks can be nested; inner {{this}} sees the inner element, outer context is restored after.",
      "template": "{{#each rows }}{{#each this }}({{ this }}){{/each}};{{/each}}",
      "context": {
        "rows": [
          [
            1,
            2
          ],
          [
            3
          ]
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "(1)(2);(3);"
      }
    },
    {
      "name": "each-parent-pointer-from-body",
      "description": "Inside an each body, ../X resolves against the outer (pre-iteration) context.",
      "template": "{{#each items }}{{ this }}-{{ ../label }}|{{/each}}",
      "context": {
        "label": "L",
        "items": [
          1,
          2
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "1-L|2-L|"
      }
    },
    {
      "name": "each-pop-restores-outer-context",
      "description": "After an each block ends, the outer context is restored for following interpolations.",
      "template": "{{ /label }}|{{#each items }}{{ this }}{{/each}}|{{ /label }}",
      "context": {
        "label": "outer",
        "items": [
          1,
          2
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "outer|12|outer"
      }
    },
    {
      "name": "each-peer-blocks",
      "description": "Two each blocks at the same level each push and pop correctly.",
      "template": "{{#each a }}{{ this }}{{/each}}-{{#each b }}{{ this }}{{/each}}",
      "context": {
        "a": [
          1,
          2
        ],
        "b": [
          3,
          4
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "12-34"
      }
    },
    {
      "name": "each-parent-pointer-exceeds-stack-errors",
      "description": "Inside an each body, a parent pointer ascending more levels than the stack has throws kbParseException.",
      "template": "{{#each items }}{{ ../../impossible }}{{/each}}",
      "context": {
        "items": [
          1
        ]
      },
      "expect": {
        "kind": "error"
      }
    },
    {
      "name": "each-non-this-on-primitive-element-is-null",
      "description": "When the array element pushed onto the stack is a primitive, any non-this identifier soft-fails and resolves to null.",
      "template": "{{#each items }}{{ x }}{{/each}}",
      "context": {
        "items": [
          "a",
          "b"
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "nullnull"
      }
    },
    {
      "name": "each-object-non-this-on-primitive-value-is-null",
      "description": "Object iteration pushes each value; if the value is a primitive, non-this identifiers soft-fail and resolve to null.",
      "template": "{{#each scores }}{{ x }}{{/each}}",
      "context": {
        "scores": {
          "a": 1
        }
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "null"
      }
    },
    {
      "name": "each-with-block-inside-each",
      "description": "A {{#with}} nested inside an {{#each}} body works and pops correctly back to the iteration element.",
      "template": "{{#each users }}{{#with profile }}{{ name }}{{/with}}|{{/each}}",
      "context": {
        "users": [
          {
            "profile": {
              "name": "A"
            }
          },
          {
            "profile": {
              "name": "B"
            }
          }
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "A|B|"
      }
    },
    {
      "name": "each-empty-array-with-empty-else-emits-nothing",
      "description": "Empty array with present-but-empty else arm emits nothing (else is rendered, but it has no content).",
      "template": "[{{#each items }}x{{else}}{{/each}}]",
      "context": {
        "items": []
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "[]"
      }
    },
    {
      "name": "each-object-non-sorted-keys-preserve-insertion-order",
      "description": "Struct iteration order is insertion order, not alphabetical: keys 'z', 'a', 'm' (already out of sorted order) render in that exact insertion sequence. A HashMap-backed port would fail this fixture.",
      "template": "{{#each letters }}{{ @key }}{{/each}}",
      "context": {
        "letters": {
          "z": 1,
          "a": 2,
          "m": 3
        }
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "zam"
      }
    },
    {
      "name": "each-doubled-adjacent-each-same-key",
      "description": "Two adjacent {{#each}} blocks over the same key iterate independently; the second is unaffected by the first.",
      "template": "{{#each xs }}({{ this }}){{/each}}{{#each xs }}[{{ this }}]{{/each}}",
      "context": {
        "xs": [
          1,
          2
        ]
      },
      "expect": {
        "kind": "output",
        "expectedOutput": "(1)(2)[1][2]"
      }
    }
  ]
}
