[
  {
    "category": "add",
    "name": "can add a value to a set",
    "html": "<div _=\"on click\n\t\t                      set :s to [] as Set\n\t\t                      add 'a' to :s\n\t\t                      add 'b' to :s\n\t\t                      add 'a' to :s\n\t\t                      put :s.size into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :s to [] as Set\n\t\t                      add 'a' to :s\n\t\t                      add 'b' to :s\n\t\t                      add 'a' to :s\n\t\t                      put :s.size into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add a value to an array",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2,3]\n\t\t                      add 4 to :arr\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2,3]\n\t\t                      add 4 to :arr\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3,4\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add class ref on a single div",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add class ref on a single form",
    "html": "<form _='on click add .foo'></form>",
    "body": "\n\t\tawait html(\"<form _='on click add .foo'></form>\");\n\t\tawait expect(find('form')).not.toHaveClass(/foo/);\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('form')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add class ref w/ double dash on a single div",
    "html": "<div _='on click add .foo--bar'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo--bar'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo--bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo--bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add class refs w/ colons and dashes",
    "html": "<div _='on click add .foo:bar-doh'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo:bar-doh'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo:bar-doh/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add css properties",
    "html": "<div style='color: blue' _='on click add {color: red; font-family: monospace}'></div>",
    "body": "\n\t\tawait html(\"<div style='color: blue' _='on click add {color: red; font-family: monospace}'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(0, 0, 255)');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t\tawait expect(find('div')).toHaveCSS('font-family', 'monospace');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add multiple class refs",
    "html": "<div _='on click add .foo .bar'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo .bar'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add non-class attributes",
    "html": "<div _='on click add [@foo=\"bar\"]'></div>",
    "body": "\n\t\tawait html(`<div _='on click add [@foo=\"bar\"]'></div>`);\n\t\tawait expect(find('div')).not.toHaveAttribute('foo');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('foo', 'bar');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add templated css properties",
    "html": "<div style='color: blue' _='on click add {color: ${}{\"red\"};}'></div>",
    "body": "\n\t\tawait html(`<div style='color: blue' _='on click add {color: ${\"$\"}{\\\"red\\\"};}'></div>`);\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(0, 0, 255)');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add to an HTMLCollection",
    "html": "<div id='trigger' _='on click add .foo to the children of #bar'></div><div id='bar'><div id='c1'></div><div id='c2'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add .foo to the children of #bar'></div>\" +\n\t\t\t\"<div id='bar'><div id='c1'></div><div id='c2'></div></div>\"\n\t\t);\n\t\tawait expect(find('#c1')).not.toHaveClass(/foo/);\n\t\tawait expect(find('#c2')).not.toHaveClass(/foo/);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#c1')).toHaveClass(/foo/);\n\t\tawait expect(find('#c2')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add to children",
    "html": "<div id='outer' _='on click add .foo to my children'><p id='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='outer' _='on click add .foo to my children'><p id='p1'></p></div>\");\n\t\tawait expect(find('#p1')).not.toHaveClass(/foo/);\n\t\tawait expect(find('#outer')).not.toHaveClass(/foo/);\n\t\tawait find('#outer').dispatchEvent('click');\n\t\tawait expect(find('#p1')).toHaveClass(/foo/);\n\t\tawait expect(find('#outer')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can add to query in me",
    "html": "<div id='outer' _='on click add .foo to <p/> in me'><p id='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='outer' _='on click add .foo to <p/> in me'><p id='p1'></p></div>\");\n\t\tawait expect(find('#p1')).not.toHaveClass(/foo/);\n\t\tawait expect(find('#outer')).not.toHaveClass(/foo/);\n\t\tawait find('#outer').dispatchEvent('click');\n\t\tawait expect(find('#p1')).toHaveClass(/foo/);\n\t\tawait expect(find('#outer')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can filter class addition via the when clause",
    "html": "<div id='trigger' _='on click add .rey to .bar when it matches .doh'></div><div id='d2' class='bar'></div><div id='d3' class='bar doh'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add .rey to .bar when it matches .doh'></div>\" +\n\t\t\t\"<div id='d2' class='bar'></div>\" +\n\t\t\t\"<div id='d3' class='bar doh'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#d2')).not.toHaveClass(/rey/);\n\t\tawait expect(find('#d3')).toHaveClass(/rey/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can filter property addition via the when clause",
    "html": "<div id='trigger' _='on click add @rey to .bar when it matches .doh'></div><div id='d2' class='bar'></div><div id='d3' class='bar doh'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add @rey to .bar when it matches .doh'></div>\" +\n\t\t\t\"<div id='d2' class='bar'></div>\" +\n\t\t\t\"<div id='d3' class='bar doh'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#d2')).not.toHaveAttribute('rey');\n\t\tawait expect(find('#d3')).toHaveAttribute('rey');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "can target another div for class ref",
    "html": "<div id='bar'></div><div id='trigger' _='on click add .foo to #bar'></div>",
    "body": "\n\t\tawait html(\"<div id='bar'></div><div id='trigger' _='on click add .foo to #bar'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo/);\n\t\tawait expect(find('#trigger')).not.toHaveClass(/foo/);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo/);\n\t\tawait expect(find('#trigger')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "supports async expressions in when clause",
    "html": "<div id='trigger' _='on click add .foo to #d2 when asyncCheck()'></div><div id='d2'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.asyncCheck = function() {\n\t\t\t\treturn new Promise(r => setTimeout(() => r(true), 10));\n\t\t\t}\n\t\t});\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add .foo to #d2 when asyncCheck()'></div>\" +\n\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "add",
    "name": "when clause result is empty when nothing matches",
    "html": "<div id='trigger' _='on click add .foo to .item when it matches .nope then if the result is empty remove @hidden from #none'></div><div id='d1' class='item'></div><div id='none' hidden></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add .foo to .item when it matches .nope then if the result is empty remove @hidden from #none'></div>\" +\n\t\t\t\"<div id='d1' class='item'></div>\" +\n\t\t\t\"<div id='none' hidden></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\t// nothing matches .nope, so result is empty -> #none shown\n\t\tawait expect(find('#none')).not.toHaveAttribute('hidden');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "add",
    "name": "when clause sets result to matched elements",
    "html": "<div id='trigger' _='on click add .foo to .item when it matches .yes then if the result is empty show #none else hide #none'></div><div id='d1' class='item yes'></div><div id='d2' class='item'></div><div id='none' style='display:none'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click add .foo to .item when it matches .yes then if the result is empty show #none else hide #none'></div>\" +\n\t\t\t\"<div id='d1' class='item yes'></div>\" +\n\t\t\t\"<div id='d2' class='item'></div>\" +\n\t\t\t\"<div id='none' style='display:none'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\t// d1 matches .yes, so result is not empty -> #none stays hidden\n\t\tawait expect(find('#none')).toBeHidden();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "append preserves existing content rather than overwriting it",
    "html": "<div _=\"on click append '<a>New Content</a>' to me\"><button id=\"btn1\">Click Me</button></div>",
    "body": "\n\t\tawait html(`<div _=\"on click append '<a>New Content</a>' to me\"><button id=\"btn1\">Click Me</button></div>`);\n\t\tawait evaluate(() => {\n\t\t\twindow.clicks = 0;\n\t\t\tdocument.querySelector('#btn1').addEventListener('click', () => { window.clicks++; });\n\t\t});\n\t\tawait evaluate(() => document.querySelector('#btn1').click());\n\t\texpect(await evaluate(() => window.clicks)).toBe(1);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toContainText(\"New Content\");\n\t\tawait evaluate(() => document.querySelector('#btn1').click());\n\t\tconst parentMatch = await evaluate(() => document.querySelector('#btn1').parentNode === document.querySelector('#work-area div'));\n\t\texpect(parentMatch).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "append to undefined ignores the undefined",
    "html": "<div id=\"id\" _=\"on click append 'bar' then append it to me\"></div>",
    "body": "\n\t\tawait html(`<div id=\"id\" _=\"on click append 'bar' then append it to me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a string to another string",
    "html": "<div _=\"on click\n                            set value to 'Hello there.' then\n                            append ' General Kenobi.' to value then\n                            set my.innerHTML to value\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n                            set value to 'Hello there.' then\n                            append ' General Kenobi.' to value then\n                            set my.innerHTML to value\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"Hello there. General Kenobi.\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value into an array",
    "html": "<div _=\"on click\n                            set value to [1,2,3]\n                            append 4 to value\n                            set my.innerHTML to value as String\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n                            set value to [1,2,3]\n                            append 4 to value\n                            set my.innerHTML to value as String\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3,4\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to 'it'",
    "html": "<div _=\"on click\n                            set result to [1,2,3]\n                            append 4\n                            put it as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n                            set result to [1,2,3]\n                            append 4\n                            put it as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3,4\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to I",
    "html": "<div _=\"on click\n                            append 'Content' to I\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n                            append 'Content' to I\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"Content\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to a DOM element",
    "html": "<div id=\"content\" _=\"on click\n                            append 'Content' to #content\"></div>",
    "body": "\n\t\tawait html(`<div id=\"content\" _=\"on click\n                            append 'Content' to #content\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"Content\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to a DOM node",
    "html": "<div _=\"on click\n                            append '<span>This is my inner HTML</span>' to me\n                            append '<b>With Tags</b>' to me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n                            append '<span>This is my inner HTML</span>' to me\n                            append '<b>With Tags</b>' to me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"This is my inner HTMLWith Tags\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to a set",
    "html": "<div _=\"on click\n\t\t                      set :s to [1,2] as Set\n\t\t                      append 3 to :s\n\t\t                      append 1 to :s\n\t\t                      put :s.size into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :s to [1,2] as Set\n\t\t                      append 3 to :s\n\t\t                      append 1 to :s\n\t\t                      put :s.size into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "can append a value to an object property",
    "html": "<div id=\"id\" _=\"on click append '_new' to my id\"></div>",
    "body": "\n\t\tawait html(`<div id=\"id\" _=\"on click append '_new' to my id\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tconst id = await evaluate(() => document.querySelector('#work-area div').id);\n\t\texpect(id).toBe(\"id_new\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "multiple appends work",
    "html": "<div id=\"id\" _=\"on click get 'foo' then append 'bar' then append 'doh' then append it to me\"></div>",
    "body": "\n\t\tawait html(`<div id=\"id\" _=\"on click get 'foo' then append 'bar' then append 'doh' then append it to me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foobardoh\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "new DOM content added by append will be live",
    "html": "<div _=\"on click make a <span.topping/> then append it to me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click make a <span.topping/> then append it to me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('span.topping')).toHaveCount(1);\n\t\tawait expect(find('span.topping')).toHaveClass(/topping/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "append",
    "name": "new content added by append will be live",
    "html": "<div _=\"on click append `<button id='b1' _='on click increment window.temp'>Test</button>` to me\"></div>",
    "body": "\n\t\tawait html(\"<div _=\\\"on click append `<button id='b1' _='on click increment window.temp'>Test</button>` to me\\\"></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('#b1').dispatchEvent('click');\n\t\tawait expect.poll(() => evaluate(() => window.temp)).toBe(1);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "askAnswer",
    "name": "confirm returns first choice on OK",
    "html": "<button _='on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out'>Go</button><div id='out'></div>",
    "body": "\n\t\tpage.on('dialog', async dialog => {\n\t\t\texpect(dialog.type()).toBe('confirm');\n\t\t\tawait dialog.accept();\n\t\t});\n\t\tawait html(\"<button _='on click answer \\\"Save?\\\" with \\\"Yes\\\" or \\\"No\\\" then put it into #out'>Go</button><div id='out'></div>\");\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"Yes\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "askAnswer",
    "name": "confirm returns second choice on cancel",
    "html": "<button _='on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out'>Go</button><div id='out'></div>",
    "body": "\n\t\tpage.on('dialog', async dialog => {\n\t\t\tawait dialog.dismiss();\n\t\t});\n\t\tawait html(\"<button _='on click answer \\\"Save?\\\" with \\\"Yes\\\" or \\\"No\\\" then put it into #out'>Go</button><div id='out'></div>\");\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"No\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "askAnswer",
    "name": "prompts and puts result in it",
    "html": "<button _='on click ask \"What is your name?\" then put it into #out'>Ask</button><div id='out'></div>",
    "body": "\n\t\tpage.on('dialog', async dialog => {\n\t\t\texpect(dialog.type()).toBe('prompt');\n\t\t\texpect(dialog.message()).toBe('What is your name?');\n\t\t\tawait dialog.accept('Alice');\n\t\t});\n\t\tawait html(\"<button _='on click ask \\\"What is your name?\\\" then put it into #out'>Ask</button><div id='out'></div>\");\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"Alice\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "askAnswer",
    "name": "returns null on cancel",
    "html": "<button _='on click ask \"Name?\" then put it into #out'>Ask</button><div id='out'></div>",
    "body": "\n\t\tpage.on('dialog', async dialog => {\n\t\t\tawait dialog.dismiss();\n\t\t});\n\t\tawait html(\"<button _='on click ask \\\"Name?\\\" then put it into #out'>Ask</button><div id='out'></div>\");\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "askAnswer",
    "name": "shows an alert",
    "html": "<button _='on click answer \"Hello!\" then put \"done\" into #out'>Go</button><div id='out'></div>",
    "body": "\n\t\tvar alertMessage = null;\n\t\tpage.on('dialog', async dialog => {\n\t\t\talertMessage = dialog.message();\n\t\t\tawait dialog.accept();\n\t\t});\n\t\tawait html(\"<button _='on click answer \\\"Hello!\\\" then put \\\"done\\\" into #out'>Go</button><div id='out'></div>\");\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"done\");\n\t\texpect(alertMessage).toBe(\"Hello!\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can declare variables in init blocks",
    "html": "<script type=text/hyperscript>\n\t\t\t\tbehavior Behave\n\t\t\t\t\tinit\n\t\t\t\t\t\tset element's foo to 1\n\t\t\t\t\t\tset element's bar to {}\n\t\t\t\t\tend\n\t\t\t\t\ton click\n\t\t\t\t\t\tincrement element's foo\n\t\t\t\t\t\tset element's bar[\"count\"] to element's foo\n\t\t\t\t\t\tput element's bar[\"count\"] into me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t</script><div _='install Behave'></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<script type=text/hyperscript>\n\t\t\t\tbehavior Behave\n\t\t\t\t\tinit\n\t\t\t\t\t\tset element's foo to 1\n\t\t\t\t\t\tset element's bar to {}\n\t\t\t\t\tend\n\t\t\t\t\ton click\n\t\t\t\t\t\tincrement element's foo\n\t\t\t\t\t\tset element's bar[\"count\"] to element's foo\n\t\t\t\t\t\tput element's bar[\"count\"] into me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t</script>` +\n\t\t\t\"<div _='install Behave'></div>\"\n\t\t)\n\t\tawait expect(find('div')).toHaveText('')\n\t\t// Wait for init to complete\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('3')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can define behaviors",
    "html": "<script type=text/hyperscript>behavior TheBehaviorWeAreDefiningForHyperscriptTestingPurposes init log 'foo' end end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior TheBehaviorWeAreDefiningForHyperscriptTestingPurposes init log 'foo' end end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tconst result = await evaluate(() => 'TheBehaviorWeAreDefiningForHyperscriptTestingPurposes' in window)\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can install behaviors",
    "html": "<script type=text/hyperscript>behavior Behave on click add .foo end end</script><div _='install Behave'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>behavior Behave on click add .foo end end</script>\" +\n\t\t\t\"<div _='install Behave'></div>\"\n\t\t)\n\t\tawait expect(find('div')).not.toHaveClass(/foo/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/foo/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can pass arguments to behaviors",
    "html": "<script type=text/hyperscript>behavior Behave(foo, bar) on click put foo + bar into me end end</script><div _='install Behave(foo: 1, bar: 1)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(foo, bar) on click put foo + bar into me end end\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div _='install Behave(foo: 1, bar: 1)'></div>\"\n\t\t)\n\t\tawait expect(find('div')).toHaveText('')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can pass element arguments to listen to in behaviors",
    "html": "<script type=text/hyperscript>behavior Behave(elt) on click from elt put 'foo' into me end end</script><button id='b1'></button><div _='install Behave(elt: #b1)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(elt) on click from elt put 'foo' into me end end\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<button id='b1'></button>\" +\n\t\t\t\"<div _='install Behave(elt: #b1)'></div>\"\n\t\t)\n\t\tawait expect(find('div')).toHaveText('')\n\t\tawait find('#b1').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('foo')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "can refer to arguments in init blocks",
    "html": "<script type=text/hyperscript>behavior Behave(elt) init put 'foo' into elt end end</script><div id='d1'></div><div _='install Behave(elt: #d1)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(elt) init put 'foo' into elt end end\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d1'></div>\" +\n\t\t\t\"<div _='install Behave(elt: #d1)'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).toHaveText('foo')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "install resolves namespaced behavior paths",
    "html": "<script type=text/hyperscript>behavior App.Widgets.Clickable on click add .clicked end end</script><div _='install App.Widgets.Clickable'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>behavior App.Widgets.Clickable on click add .clicked end end</script>\" +\n\t\t\t\"<div _='install App.Widgets.Clickable'></div>\"\n\t\t)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "install throws when the behavior path does not exist",
    "html": "<div _='install NoSuchBehavior'></div>",
    "body": "\n\t\tvar messages = []\n\t\tpage.on('console', m => { if (m.type() === 'error') messages.push(m.text()) })\n\t\tpage.on('pageerror', e => messages.push(e.message))\n\t\tawait html(\"<div _='install NoSuchBehavior'></div>\")\n\t\t// install() runs during processNode; errors are surfaced via console.error\n\t\tvar combined = messages.join('\\n')\n\t\texpect(combined).toMatch(/No such behavior defined as NoSuchBehavior/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "install throws when the path resolves to a non-function",
    "html": "<div _='install NotABehavior'></div>",
    "body": "\n\t\tvar messages = []\n\t\tpage.on('console', m => { if (m.type() === 'error') messages.push(m.text()) })\n\t\tpage.on('pageerror', e => messages.push(e.message))\n\t\tawait evaluate(() => { window.NotABehavior = { hello: 'world' } })\n\t\tawait html(\"<div _='install NotABehavior'></div>\")\n\t\tvar combined = messages.join('\\n')\n\t\texpect(combined).toMatch(/NotABehavior is not a behavior/)\n\t\tawait evaluate(() => { delete window.NotABehavior })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "behavior",
    "name": "supports init blocks in behaviors",
    "html": "<script type=text/hyperscript>behavior Behave init add .foo to me end</script><div _='install Behave'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>behavior Behave init add .foo to me end</script>\" +\n\t\t\t\"<div _='install Behave'></div>\"\n\t\t)\n\t\tawait expect(find('div')).toHaveClass(/foo/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "\"with\" is a synonym for \"and\"",
    "html": "<input type=\"text\" id=\"city-input\" value=\"Paris\" /><span _=\"bind $city to #city-input.value end\n\t\t\t          when $city changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"city-input\" value=\"Paris\" />` +\n\t\t\t`<span _=\"bind $city to #city-input.value end\n\t\t\t          when $city changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('Paris')\n\n\t\tawait run(\"set $city to 'London'\")\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('city-input').value)).toBe('London')\n\t\tawait evaluate(() => { delete window.$city })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "attribute bound to another element input value",
    "html": "<input type=\"text\" id=\"title-input\" value=\"Hello\" /><h1 _=\"bind @data-title and #title-input's value\"></h1>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"title-input\" value=\"Hello\" />` +\n\t\t\t`<h1 _=\"bind @data-title and #title-input's value\"></h1>`\n\t\t)\n\t\tawait expect.poll(() => find('h1').getAttribute('data-title')).toBe('Hello')\n\n\t\tawait find('#title-input').fill('World')\n\t\tawait expect.poll(() => find('h1').getAttribute('data-title')).toBe('World')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "bind element to element: both sides auto-detect",
    "html": "<input type=\"range\" id=\"range-slider\" value=\"50\" /><input type=\"number\" _=\"bind me to #range-slider\" />",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"range\" id=\"range-slider\" value=\"50\" />` +\n\t\t\t`<input type=\"number\" _=\"bind me to #range-slider\" />`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('#work-area input[type=number]').valueAsNumber)).toBe(50)\n\n\t\tawait evaluate(() => {\n\t\t\tvar slider = document.getElementById('range-slider')\n\t\t\tslider.value = '75'\n\t\t\tslider.dispatchEvent(new Event('input', { bubbles: true }))\n\t\t})\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area input[type=number]').valueAsNumber)\n\t\t).toBe(75)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "bind to contenteditable element auto-detects textContent",
    "html": "<div contenteditable=\"true\" _=\"bind $text to me\">initial</div>",
    "body": "\n\t\tawait html(`<div contenteditable=\"true\" _=\"bind $text to me\">initial</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$text)).toBe('initial')\n\n\t\tawait run(\"set $text to 'updated'\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div[contenteditable]').textContent)\n\t\t).toBe('updated')\n\t\tawait evaluate(() => { delete window.$text })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "bind to custom element with value property auto-detects value",
    "html": "<test-input _=\"bind $custom to me\"></test-input>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\tif (!customElements.get('test-input')) {\n\t\t\t\tclass TestInput extends HTMLElement {\n\t\t\t\t\tconstructor() { super(); this._value = ''; }\n\t\t\t\t\tget value() { return this._value; }\n\t\t\t\t\tset value(v) {\n\t\t\t\t\t\tthis._value = v;\n\t\t\t\t\t\tthis.dispatchEvent(new Event('input', { bubbles: true }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcustomElements.define('test-input', TestInput);\n\t\t\t}\n\t\t})\n\n\t\tawait html(`<test-input _=\"bind $custom to me\"></test-input>`)\n\t\tawait run(\"set $custom to 'hello'\")\n\t\tawait expect.poll(() => evaluate(() => document.querySelector('#work-area test-input').value)).toBe('hello')\n\t\tawait evaluate(() => { delete window.$custom })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "bind variable to checkbox by id auto-detects checked",
    "html": "<input type=\"checkbox\" id=\"agree-cb\" /><div _=\"bind $agreed to #agree-cb\"></div>",
    "body": "\n\t\tawait run(\"set $agreed to false\")\n\t\tawait html(\n\t\t\t`<input type=\"checkbox\" id=\"agree-cb\" />` +\n\t\t\t`<div _=\"bind $agreed to #agree-cb\"></div>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.getElementById('agree-cb').checked)).toBe(false)\n\n\t\tawait run(\"set $agreed to true\")\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('agree-cb').checked)).toBe(true)\n\t\tawait evaluate(() => { delete window.$agreed })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "bind variable to element by id auto-detects value",
    "html": "<input type=\"text\" id=\"name-field\" value=\"\" /><div _=\"bind $name to #name-field\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"name-field\" value=\"\" />` +\n\t\t\t`<div _=\"bind $name to #name-field\"></div>`\n\t\t)\n\t\tawait run(\"set $name to 'Alice'\")\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('name-field').value)).toBe('Alice')\n\n\t\tawait evaluate(() => {\n\t\t\tvar input = document.getElementById('name-field')\n\t\t\tinput.value = 'Bob'\n\t\t\tinput.dispatchEvent(new Event('input', { bubbles: true }))\n\t\t})\n\t\tawait expect.poll(() => evaluate(() => window.$name)).toBe('Bob')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "bind variable to number input by id auto-detects valueAsNumber",
    "html": "<input type=\"number\" id=\"qty-input\" /><div _=\"bind $qty to #qty-input\"></div>",
    "body": "\n\t\tawait run(\"set $qty to 5\")\n\t\tawait html(\n\t\t\t`<input type=\"number\" id=\"qty-input\" />` +\n\t\t\t`<div _=\"bind $qty to #qty-input\"></div>`\n\t\t)\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('qty-input').valueAsNumber)).toBe(5)\n\t\texpect(await evaluate(() => typeof window.$qty)).toBe('number')\n\t\tawait evaluate(() => { delete window.$qty })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "boolean bind to aria-* attribute uses \"true\"/\"false\" strings",
    "html": "<div _=\"bind $isHidden and @aria-hidden\"></div>",
    "body": "\n\t\tawait run(\"set $isHidden to true\")\n\t\tawait html(`<div _=\"bind $isHidden and @aria-hidden\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveAttribute('aria-hidden', 'true')\n\n\t\tawait run(\"set $isHidden to false\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveAttribute('aria-hidden', 'false')\n\t\tawait evaluate(() => { delete window.$isHidden })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "boolean bind to attribute uses presence/absence",
    "html": "<div _=\"bind $isEnabled and @data-active\"></div>",
    "body": "\n\t\tawait run(\"set $isEnabled to true\")\n\t\tawait html(`<div _=\"bind $isEnabled and @data-active\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveAttribute('data-active', '')\n\n\t\tawait run(\"set $isEnabled to false\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() =>\n\t\t\tdocument.querySelector('#work-area div').hasAttribute('data-active')\n\t\t)).toBe(false)\n\t\tawait evaluate(() => { delete window.$isEnabled })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "class bound to another element checkbox",
    "html": "<input type=\"checkbox\" id=\"dark-toggle\" /><div _=\"bind .dark and #dark-toggle's checked\">test</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"checkbox\" id=\"dark-toggle\" />` +\n\t\t\t`<div _=\"bind .dark and #dark-toggle's checked\">test</div>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).not.toHaveClass('dark')\n\n\t\tawait find('#dark-toggle').check()\n\t\tawait expect.poll(() => find('div').getAttribute('class')).toContain('dark')\n\n\t\tawait find('#dark-toggle').uncheck()\n\t\tawait expect.poll(() => find('div').getAttribute('class') || '').not.toContain('dark')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "clicking a radio sets the variable to its value",
    "html": "<input type=\"radio\" name=\"color\" value=\"red\" _=\"bind $color to me\" /><input type=\"radio\" name=\"color\" value=\"blue\" _=\"bind $color to me\" /><input type=\"radio\" name=\"color\" value=\"green\" _=\"bind $color to me\" /><span _=\"when $color changes put it into me\"></span>",
    "body": "\n\t\tawait run(\"set $color to 'red'\")\n\t\tawait html(\n\t\t\t`<input type=\"radio\" name=\"color\" value=\"red\" _=\"bind $color to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"color\" value=\"blue\" _=\"bind $color to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"color\" value=\"green\" _=\"bind $color to me\" />` +\n\t\t\t`<span _=\"when $color changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('red')\n\n\t\tawait find('input[value=\"blue\"]').click()\n\t\tawait expect.poll(() => find('span').textContent()).toBe('blue')\n\n\t\tawait find('input[value=\"green\"]').click()\n\t\tawait expect.poll(() => find('span').textContent()).toBe('green')\n\t\tawait evaluate(() => { delete window.$color })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "dedup prevents infinite loop in two-way bind",
    "html": "<div _=\"bind $color and @data-color\"></div>",
    "body": "\n\t\tawait run(\"set $color to 'red'\")\n\t\tawait html(`<div _=\"bind $color and @data-color\"></div>`)\n\t\tawait expect(find('div')).toHaveAttribute('data-color', 'red')\n\n\t\tawait run(\"set $color to 'blue'\")\n\t\tawait expect(find('div')).toHaveAttribute('data-color', 'blue')\n\t\tawait expect.poll(() => evaluate(() => window.$color)).toBe('blue')\n\t\tawait evaluate(() => { delete window.$color })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "external JS property write does not sync (known limitation)",
    "html": "<input type=\"text\" value=\"original\" _=\"bind $searchTerm to me\" /><span _=\"when $searchTerm changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" value=\"original\" _=\"bind $searchTerm to me\" />` +\n\t\t\t`<span _=\"when $searchTerm changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('original')\n\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area input').value = 'from-javascript'\n\t\t})\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await evaluate(() => window.$searchTerm)).toBe('original')\n\t\tawait evaluate(() => { delete window.$searchTerm })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "external class change syncs back to variable",
    "html": "<div _=\"bind .dark and $darkMode\">test</div>",
    "body": "\n\t\tawait run(\"set $darkMode to false\")\n\t\tawait html(`<div _=\"bind .dark and $darkMode\">test</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').classList.add('dark'))\n\t\tawait expect.poll(() => evaluate(() => window.$darkMode)).toBe(true)\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').classList.remove('dark'))\n\t\tawait expect.poll(() => evaluate(() => window.$darkMode)).toBe(false)\n\t\tawait evaluate(() => { delete window.$darkMode })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "form reset listener is removed on cleanup",
    "html": "<form><input type=\"text\" id=\"binput\" value=\"initial\" _=\"bind $val to me\" /><button type=\"reset\">Reset</button></form>",
    "body": "\n\t\tawait run(\"set $val to 'initial'\")\n\t\tawait html(\n\t\t\t`<form>` +\n\t\t\t`<input type=\"text\" id=\"binput\" value=\"initial\" _=\"bind $val to me\" />` +\n\t\t\t`<button type=\"reset\">Reset</button>` +\n\t\t\t`</form>`\n\t\t)\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\n\t\t// Change value then cleanup the input\n\t\tawait run(\"set $val to 'changed'\")\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\t\tawait evaluate(() => {\n\t\t\tvar input = document.querySelector('#work-area #binput')\n\t\t\t_hyperscript.internals.runtime.cleanup(input)\n\t\t})\n\n\t\t// Reset the form - $val should NOT revert since listener was removed\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area form').reset()\n\t\t})\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\t\tvar val = await run(\"$val\")\n\t\texpect(val).toBe('changed')\n\t\tawait evaluate(() => { delete window.$val })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "form.reset() syncs variable back to default value",
    "html": "<form id=\"test-form\">  <input type=\"text\" value=\"default\" _=\"bind $formField to me\" /></form><span _=\"when $formField changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<form id=\"test-form\">` +\n\t\t\t`  <input type=\"text\" value=\"default\" _=\"bind $formField to me\" />` +\n\t\t\t`</form>` +\n\t\t\t`<span _=\"when $formField changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('default')\n\n\t\tawait find('input').fill('user typed this')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('user typed this')\n\n\t\tawait evaluate(() => document.getElementById('test-form').reset())\n\t\tawait expect.poll(() => find('span').textContent()).toBe('default')\n\t\texpect(await evaluate(() => window.$formField)).toBe('default')\n\t\tawait evaluate(() => { delete window.$formField })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "init: right side wins - attribute (Y) initializes variable (X)",
    "html": "<div data-color=\"red\" _=\"bind $color to @data-color\"></div>",
    "body": "\n\t\tawait html(`<div data-color=\"red\" _=\"bind $color to @data-color\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$color)).toBe('red')\n\t\tawait evaluate(() => { delete window.$color })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "init: right side wins - class (Y) drives variable (X)",
    "html": "<div class=\"dark\" _=\"bind $isDark to .dark\"></div>",
    "body": "\n\t\tawait run(\"set $isDark to false\")\n\t\tawait html(`<div class=\"dark\" _=\"bind $isDark to .dark\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$isDark)).toBe(true)\n\t\tawait evaluate(() => { delete window.$isDark })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "init: right side wins - input value (Y) overwrites variable (X)",
    "html": "<input type=\"text\" value=\"Bob\" _=\"bind $name to my value\" />",
    "body": "\n\t\tawait run(\"set $name to 'Alice'\")\n\t\tawait html(`<input type=\"text\" value=\"Bob\" _=\"bind $name to my value\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('#work-area input').value)).toBe('Bob')\n\t\texpect(await evaluate(() => window.$name)).toBe('Bob')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "init: right side wins - variable (Y) drives class (X)",
    "html": "<div _=\"bind .dark to $isDark\"></div>",
    "body": "\n\t\tawait run(\"set $isDark to true\")\n\t\tawait html(`<div _=\"bind .dark to $isDark\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect.poll(() => find('div').getAttribute('class')).toContain('dark')\n\t\tawait evaluate(() => { delete window.$isDark })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "init: right side wins - variable (Y) initializes attribute (X)",
    "html": "<div _=\"bind @data-theme to $theme\"></div>",
    "body": "\n\t\tawait run(\"set $theme to 'dark'\")\n\t\tawait html(`<div _=\"bind @data-theme to $theme\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveAttribute('data-theme', 'dark')\n\t\tawait evaluate(() => { delete window.$theme })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "init: right side wins - variable (Y) overwrites input value (X)",
    "html": "<input type=\"text\" value=\"Bob\" _=\"bind my value to $name\" />",
    "body": "\n\t\tawait run(\"set $name to 'Alice'\")\n\t\tawait html(`<input type=\"text\" value=\"Bob\" _=\"bind my value to $name\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('#work-area input').value)).toBe('Alice')\n\t\texpect(await evaluate(() => window.$name)).toBe('Alice')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "initial value checks the correct radio on load",
    "html": "<input type=\"radio\" name=\"fruit\" value=\"apple\" _=\"bind $fruit to me\" /><input type=\"radio\" name=\"fruit\" value=\"banana\" _=\"bind $fruit to me\" /><input type=\"radio\" name=\"fruit\" value=\"cherry\" _=\"bind $fruit to me\" />",
    "body": "\n\t\tawait run(\"set $fruit to 'banana'\")\n\t\tawait html(\n\t\t\t`<input type=\"radio\" name=\"fruit\" value=\"apple\" _=\"bind $fruit to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"fruit\" value=\"banana\" _=\"bind $fruit to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"fruit\" value=\"cherry\" _=\"bind $fruit to me\" />`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"apple\"]').checked)).toBe(false)\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"banana\"]').checked)).toBe(true)\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"cherry\"]').checked)).toBe(false)\n\t\tawait evaluate(() => { delete window.$fruit })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "of-expression: bind $var to value of #input",
    "html": "<input type=\"text\" id=\"of-input\" value=\"initial\" /><div _=\"bind $search to value of #of-input\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"of-input\" value=\"initial\" />` +\n\t\t\t`<div _=\"bind $search to value of #of-input\"></div>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$search)).toBe('initial')\n\t\tawait evaluate(() => { delete window.$search })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "possessive attribute: bind $var and my @data-label",
    "html": "<div _=\"bind $label and my @data-label\"></div>",
    "body": "\n\t\tawait run(\"set $label to 'important'\")\n\t\tawait html(`<div _=\"bind $label and my @data-label\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveAttribute('data-label', 'important')\n\n\t\tawait run(\"set $label to 'normal'\")\n\t\tawait expect.poll(() => find('div').getAttribute('data-label')).toBe('normal')\n\t\tawait evaluate(() => { delete window.$label })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "possessive property: bind $var to my value",
    "html": "<input type=\"text\" value=\"hello\" _=\"bind $myVal to my value\" />",
    "body": "\n\t\tawait html(`<input type=\"text\" value=\"hello\" _=\"bind $myVal to my value\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$myVal)).toBe('hello')\n\n\t\tawait find('input').fill('world')\n\t\tawait expect.poll(() => evaluate(() => window.$myVal)).toBe('world')\n\t\tawait evaluate(() => { delete window.$myVal })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "radio change listener is removed on cleanup",
    "html": "<input type=\"radio\" name=\"color\" value=\"red\" _=\"bind $color to me\" checked /><input type=\"radio\" name=\"color\" value=\"blue\" _=\"bind $color to me\" />",
    "body": "\n\t\tawait run(\"set $color to 'red'\")\n\t\tawait html(\n\t\t\t`<input type=\"radio\" name=\"color\" value=\"red\" _=\"bind $color to me\" checked />` +\n\t\t\t`<input type=\"radio\" name=\"color\" value=\"blue\" _=\"bind $color to me\" />`\n\t\t)\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\n\t\t// Cleanup the blue radio\n\t\tvar blueRadio = await evaluate(() => {\n\t\t\tvar blue = document.querySelector('#work-area input[value=blue]')\n\t\t\t_hyperscript.internals.runtime.cleanup(blue)\n\t\t\treturn true\n\t\t})\n\n\t\t// Click the cleaned-up blue radio - $color should NOT change\n\t\tawait evaluate(() => {\n\t\t\tvar blue = document.querySelector('#work-area input[value=blue]')\n\t\t\tblue.checked = true\n\t\t\tblue.dispatchEvent(new Event('change'))\n\t\t})\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\t\tvar color = await run(\"$color\")\n\t\texpect(color).toBe('red')\n\t\tawait evaluate(() => { delete window.$color })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "right side wins on class init",
    "html": "<div _=\"bind .highlight to $highlighted\">test</div>",
    "body": "\n\t\tawait run(\"set $highlighted to true\")\n\t\tawait html(`<div _=\"bind .highlight to $highlighted\">test</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveClass('highlight')\n\t\tawait evaluate(() => { delete window.$highlighted })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "right side wins on init: input (Y) initializes variable (X)",
    "html": "<input type=\"text\" value=\"Bob\" _=\"bind $name to me\" />",
    "body": "\n\t\tawait html(`<input type=\"text\" value=\"Bob\" _=\"bind $name to me\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$name)).toBe('Bob')\n\t\texpect(await evaluate(() => document.querySelector('#work-area input').value)).toBe('Bob')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "right side wins on init: variable (Y) initializes input (X)",
    "html": "<input type=\"text\" value=\"Bob\" _=\"bind me to $name\" />",
    "body": "\n\t\tawait run(\"set $name to 'Alice'\")\n\t\tawait html(`<input type=\"text\" value=\"Bob\" _=\"bind me to $name\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => window.$name)).toBe('Alice')\n\t\texpect(await evaluate(() => document.querySelector('#work-area input').value)).toBe('Alice')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "same value does not re-set input (prevents cursor jump)",
    "html": "<input type=\"text\" value=\"hello\" _=\"bind $message to me\" />",
    "body": "\n\t\tawait html(`<input type=\"text\" value=\"hello\" _=\"bind $message to me\" />`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\n\t\tconst setterWasCalled = await evaluate(() => {\n\t\t\tvar input = document.querySelector('#work-area input')\n\t\t\tvar called = false\n\t\t\tvar desc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')\n\t\t\tObject.defineProperty(input, 'value', {\n\t\t\t\tget: desc.get,\n\t\t\t\tset: function(v) { called = true; desc.set.call(this, v) },\n\t\t\t\tconfigurable: true\n\t\t\t})\n\t\t\twindow.$message = 'hello' // same value\n\t\t\treturn new Promise(resolve => {\n\t\t\t\tsetTimeout(() => { delete input.value; resolve(called) }, 100)\n\t\t\t})\n\t\t})\n\t\texpect(setterWasCalled).toBe(false)\n\t\tawait evaluate(() => { delete window.$message })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "setting variable programmatically checks the matching radio",
    "html": "<input type=\"radio\" name=\"size\" value=\"small\" _=\"bind $size to me\" /><input type=\"radio\" name=\"size\" value=\"medium\" _=\"bind $size to me\" /><input type=\"radio\" name=\"size\" value=\"large\" _=\"bind $size to me\" />",
    "body": "\n\t\tawait run(\"set $size to 'small'\")\n\t\tawait html(\n\t\t\t`<input type=\"radio\" name=\"size\" value=\"small\" _=\"bind $size to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"size\" value=\"medium\" _=\"bind $size to me\" />` +\n\t\t\t`<input type=\"radio\" name=\"size\" value=\"large\" _=\"bind $size to me\" />`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"small\"]').checked)).toBe(true)\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"medium\"]').checked)).toBe(false)\n\n\t\tawait run(\"set $size to 'large'\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('input[value=\"large\"]').checked)\n\t\t).toBe(true)\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"small\"]').checked)).toBe(false)\n\t\texpect(await evaluate(() => document.querySelector('input[value=\"medium\"]').checked)).toBe(false)\n\t\tawait evaluate(() => { delete window.$size })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "shorthand on checkbox binds to checked",
    "html": "<input type=\"checkbox\" _=\"bind $isDarkMode to me\" /><span _=\"when $isDarkMode changes put it into me\"></span>",
    "body": "\n\t\tawait run(\"set $isDarkMode to false\")\n\t\tawait html(\n\t\t\t`<input type=\"checkbox\" _=\"bind $isDarkMode to me\" />` +\n\t\t\t`<span _=\"when $isDarkMode changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('false')\n\n\t\tawait find('input').check()\n\t\tawait expect.poll(() => find('span').textContent()).toBe('true')\n\n\t\tawait run(\"set $isDarkMode to false\")\n\t\tawait expect.poll(() => evaluate(() => document.querySelector('#work-area input').checked)).toBe(false)\n\t\tawait evaluate(() => { delete window.$isDarkMode })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "shorthand on select binds to value",
    "html": "<select _=\"bind $country to me\">\n\t\t\t   <option value=\"us\">United States</option>\n\t\t\t   <option value=\"uk\">United Kingdom</option>\n\t\t\t   <option value=\"fr\">France</option>\n\t\t\t </select><span _=\"when $country changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<select _=\"bind $country to me\">\n\t\t\t   <option value=\"us\">United States</option>\n\t\t\t   <option value=\"uk\">United Kingdom</option>\n\t\t\t   <option value=\"fr\">France</option>\n\t\t\t </select>` +\n\t\t\t`<span _=\"when $country changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('us')\n\n\t\tawait find('select').selectOption('uk')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('uk')\n\t\tawait evaluate(() => { delete window.$country })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "shorthand on text input binds to value",
    "html": "<input type=\"text\" value=\"hello\"\n\t\t\t        _=\"bind $greeting to me end\n\t\t\t           when $greeting changes put it into next <span/>\"></input><span></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" value=\"hello\"\n\t\t\t        _=\"bind $greeting to me end\n\t\t\t           when $greeting changes put it into next <span/>\"></input>` +\n\t\t\t`<span></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('hello')\n\n\t\tawait find('input').fill('goodbye')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('goodbye')\n\n\t\tawait run(\"set $greeting to 'hey'\")\n\t\tawait expect.poll(() => evaluate(() => document.querySelector('#work-area input').value)).toBe('hey')\n\t\tawait evaluate(() => { delete window.$greeting })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "shorthand on textarea binds to value",
    "html": "<textarea _=\"bind $bio to me\">Hello world</textarea><span _=\"when $bio changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<textarea _=\"bind $bio to me\">Hello world</textarea>` +\n\t\t\t`<span _=\"when $bio changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('Hello world')\n\n\t\tawait find('textarea').fill('New bio')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('New bio')\n\t\tawait evaluate(() => { delete window.$bio })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "shorthand on type=number preserves number type",
    "html": "<input type=\"number\" _=\"bind $price to me\" /><span _=\"when $price changes put it into me\"></span>",
    "body": "\n\t\tawait run(\"set $price to 42\")\n\t\tawait html(\n\t\t\t`<input type=\"number\" _=\"bind $price to me\" />` +\n\t\t\t`<span _=\"when $price changes put it into me\"></span>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await evaluate(() => typeof window.$price)).toBe('number')\n\t\tawait expect(find('span')).toHaveText('42')\n\t\tawait evaluate(() => { delete window.$price })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "style bind is one-way: variable drives style, not vice versa",
    "html": "<div _=\"bind $opacity and *opacity\">visible</div>",
    "body": "\n\t\tawait run(\"set $opacity to 1\")\n\t\tawait html(`<div _=\"bind $opacity and *opacity\">visible</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\n\t\tawait run(\"set $opacity to 0.3\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div').style.opacity)\n\t\t).toBe('0.3')\n\n\t\t// Changing style directly does NOT update the variable\n\t\tawait evaluate(() => document.querySelector('#work-area div').style.opacity = '0.9')\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await evaluate(() => window.$opacity)).toBe(0.3)\n\t\tawait evaluate(() => { delete window.$opacity })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "syncs variable and attribute in both directions",
    "html": "<div _=\"bind $theme and @data-theme\"></div>",
    "body": "\n\t\tawait run(\"set $theme to 'light'\")\n\t\tawait html(`<div _=\"bind $theme and @data-theme\"></div>`)\n\t\tawait expect(find('div')).toHaveAttribute('data-theme', 'light')\n\n\t\tawait run(\"set $theme to 'dark'\")\n\t\tawait expect(find('div')).toHaveAttribute('data-theme', 'dark')\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute('data-theme', 'auto'))\n\t\tawait expect.poll(() => evaluate(() => window.$theme), { timeout: 5000 }).toBe('auto')\n\t\tawait evaluate(() => { delete window.$theme })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "syncs variable and input value in both directions",
    "html": "<input type=\"text\" id=\"name-input\" value=\"Alice\" /><span _=\"bind $name and #name-input.value end\n\t\t\t          when $name changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"name-input\" value=\"Alice\" />` +\n\t\t\t`<span _=\"bind $name and #name-input.value end\n\t\t\t          when $name changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('Alice')\n\n\t\t// User types -> variable updates\n\t\tawait evaluate(() => {\n\t\t\tvar input = document.getElementById('name-input')\n\t\t\tinput.value = 'Bob'\n\t\t\tinput.dispatchEvent(new Event('input', { bubbles: true }))\n\t\t})\n\t\tawait expect(find('span')).toHaveText('Bob')\n\n\t\t// Variable changes -> input updates\n\t\tawait run(\"set $name to 'Charlie'\")\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('name-input').value)).toBe('Charlie')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "bind",
    "name": "two inputs synced via bind",
    "html": "<input type=\"range\" id=\"slider\" value=\"50\" /><input type=\"number\" _=\"bind my value and #slider's value\" />",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"range\" id=\"slider\" value=\"50\" />` +\n\t\t\t`<input type=\"number\" _=\"bind my value and #slider's value\" />`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await evaluate(() => document.querySelector('#work-area input[type=number]').value)).toBe('50')\n\n\t\tawait evaluate(() => {\n\t\t\tvar slider = document.getElementById('slider')\n\t\t\tslider.value = '75'\n\t\t\tslider.dispatchEvent(new Event('input', {bubbles: true}))\n\t\t})\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area input[type=number]').value)\n\t\t).toBe('75')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "unsupported element: bind to plain div errors",
    "html": "",
    "body": "\n\t\tconst error = await evaluate(() => {\n\t\t\treturn new Promise(resolve => {\n\t\t\t\tvar origError = console.error\n\t\t\t\tconsole.error = function(msg) {\n\t\t\t\t\tif (typeof msg === 'string' && msg.includes('bind cannot auto-detect')) {\n\t\t\t\t\t\tresolve(msg)\n\t\t\t\t\t}\n\t\t\t\t\torigError.apply(console, arguments)\n\t\t\t\t}\n\t\t\t\tvar wa = document.getElementById('work-area')\n\t\t\t\twa.innerHTML = '<div _=\"bind $nope to me\"></div>'\n\t\t\t\t_hyperscript.processNode(wa)\n\t\t\t\tsetTimeout(() => { console.error = origError; resolve(null) }, 500)\n\t\t\t})\n\t\t})\n\t\texpect(await evaluate(() => window.$nope)).toBeUndefined()\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "bind",
    "name": "variable drives class: setting variable adds/removes class",
    "html": "<div _=\"bind .dark and $darkMode\">test</div>",
    "body": "\n\t\tawait run(\"set $darkMode to false\")\n\t\tawait html(`<div _=\"bind .dark and $darkMode\">test</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).not.toHaveClass('dark')\n\n\t\tawait run(\"set $darkMode to true\")\n\t\tawait expect.poll(() => find('div').getAttribute('class')).toContain('dark')\n\n\t\tawait run(\"set $darkMode to false\")\n\t\tawait expect.poll(() => find('div').getAttribute('class') || '').not.toContain('dark')\n\t\tawait evaluate(() => { delete window.$darkMode })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "breakpoint",
    "name": "parses as a top-level command",
    "html": "",
    "body": "\n\t\texpect(await error(\"breakpoint\")).toBeNull()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "breakpoint",
    "name": "parses inside an event handler",
    "html": "",
    "body": "\n\t\t// Parsing-only check: installing the handler must not throw.\n\t\texpect(await error(\"on click breakpoint end\")).toBeNull()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "call",
    "name": "call functions that return promises are waited on",
    "html": "<div _='on click call promiseAnInt() then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.promiseAnInt = function () {\n\t\t\t\treturn new Promise(function (finish) {\n\t\t\t\t\twindow.finish = finish;\n\t\t\t\t});\n\t\t\t};\n\t\t});\n\t\tawait html(\"<div _='on click call promiseAnInt() then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait evaluate(() => window.finish(42));\n\t\tawait expect(find('div')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "call",
    "name": "can call functions w/ dollar signs",
    "html": "<div _='on click call $()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.called = false;\n\t\t\twindow.$ = function () { window.called = true; };\n\t\t});\n\t\tawait html(\"<div _='on click call $()'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.called)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "call",
    "name": "can call functions w/ underscores",
    "html": "<div _='on click call global_function()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.called = false;\n\t\t\twindow.global_function = function () { window.called = true; };\n\t\t});\n\t\tawait html(\"<div _='on click call global_function()'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.called)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "call",
    "name": "can call global javascript functions",
    "html": "<div _='on click call globalFunction(\"foo\")'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.calledWith = null;\n\t\t\twindow.globalFunction = function (val) { window.calledWith = val; };\n\t\t});\n\t\tawait html(\"<div _='on click call globalFunction(\\\"foo\\\")'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.calledWith)).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "call",
    "name": "can call javascript instance functions",
    "html": "<div id='d1' _='on click call document.getElementById(\"d1\") then                                          put it into window.results'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click call document.getElementById(\\\"d1\\\") then\" +\n\t\t\t\t\"                                          put it into window.results'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst match = await evaluate(() => window.results === document.querySelector('#d1'));\n\t\texpect(match).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "call",
    "name": "can call no argument functions",
    "html": "<div _='on click call globalFunction()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.called = false;\n\t\t\twindow.globalFunction = function () { window.called = true; };\n\t\t});\n\t\tawait html(\"<div _='on click call globalFunction()'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.called)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/api",
    "name": "processNodes does not reinitialize a node already processed",
    "html": "<div _='on click set window.global_int to window.global_int + 1'></div>",
    "body": "\n\t\tawait evaluate(() => window.global_int = 0);\n\t\tawait html(\"<div _='on click set window.global_int to window.global_int + 1'></div>\");\n\t\texpect(await evaluate(() => window.global_int)).toBe(0);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect.poll(() => evaluate(() => window.global_int)).toBe(1);\n\t\tawait evaluate(() => _hyperscript.processNode(document.querySelector('#work-area div')));\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect.poll(() => evaluate(() => window.global_int)).toBe(2);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/asyncError",
    "name": "rejected promise stops execution",
    "html": "<button _=\"on click\n\t\t\t\tcall failAsync()\n\t\t\t\tput 'should not reach' into #out\n\t\t\t\">Go</button>\n\t\t\t<div id='out'>original</div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.failAsync = function() {\n\t\t\t\treturn Promise.reject(new Error(\"boom\"));\n\t\t\t}\n\t\t});\n\t\tawait html(\n\t\t\t`<button _=\"on click\n\t\t\t\tcall failAsync()\n\t\t\t\tput 'should not reach' into #out\n\t\t\t\">Go</button>\n\t\t\t<div id='out'>original</div>`\n\t\t);\n\t\tawait find('button').click();\n\t\tawait new Promise(r => setTimeout(r, 200));\n\t\tawait expect(find('#out')).toHaveText(\"original\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/asyncError",
    "name": "rejected promise triggers catch block",
    "html": "<button _=\"on click\n\t\t\t\tcall failAsync()\n\t\t\t\tput 'unreachable' into #out\n\t\t\tcatch e\n\t\t\t\tput e.message into #out\n\t\t\t\">Go</button>\n\t\t\t<div id='out'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.failAsync = function() {\n\t\t\t\treturn Promise.reject(new Error(\"boom\"));\n\t\t\t}\n\t\t});\n\t\tawait html(\n\t\t\t`<button _=\"on click\n\t\t\t\tcall failAsync()\n\t\t\t\tput 'unreachable' into #out\n\t\t\tcatch e\n\t\t\t\tput e.message into #out\n\t\t\t\">Go</button>\n\t\t\t<div id='out'></div>`\n\t\t);\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"boom\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/bootstrap",
    "name": "can call functions",
    "html": "<div _='on click call globalFunction(\"foo\")'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.calledWith = null;\n\t\t\twindow.globalFunction = function (val) { window.calledWith = val; };\n\t\t});\n\t\tawait html(\"<div _='on click call globalFunction(\\\"foo\\\")'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.calledWith)).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can change non-class properties",
    "html": "<div _='on click add [@foo=\"bar\"]'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add [@foo=\\\"bar\\\"]'></div>\");\n\t\tawait expect(find('div')).not.toHaveAttribute('foo');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('foo', 'bar');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can respond to events on other elements",
    "html": "<div id='bar'></div><div _='on click from #bar                              add .clicked'></div>",
    "body": "\n\t\tawait html(\"<div id='bar'></div>\" +\n\t\t\t\"<div _='on click from #bar \" + \"                             add .clicked'></div>\");\n\t\tawait expect(find('div:nth-of-type(2)')).not.toHaveClass(/clicked/);\n\t\tawait find('#bar').dispatchEvent('click');\n\t\tawait expect(find('div:nth-of-type(2)')).toHaveClass(/clicked/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can send events",
    "html": "<div _='on click send foo to #bar'></div><div id='bar' _='on foo add .foo-sent'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo to #bar'></div><div id='bar' _='on foo add .foo-sent'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo-sent/);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can send events with args",
    "html": "<div _='on click send foo(x:42) to #bar'></div><div id='bar' _='on foo put event.detail.x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo(x:42) to #bar'></div><div id='bar' _='on foo put event.detail.x into my.innerHTML'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can set properties",
    "html": "<div id='d1' _='on click put \"foo\" into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" into #d1.innerHTML'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can set styles",
    "html": "<div _='on click put \"red\" into my.style.color'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"red\\\" into my.style.color'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can take a class from other elements",
    "html": "<div class='divs foo'></div><div class='divs' _='on click take .foo from .divs'></div><div class='divs'></div>",
    "body": "\n\t\tawait html(\"<div class='divs foo'></div>\" +\n\t\t\t\"<div class='divs' _='on click take .foo from .divs'></div>\" +\n\t\t\t\"<div class='divs'></div>\");\n\t\tawait find('.divs').nth(1).dispatchEvent('click');\n\t\tawait expect(find('.divs').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('.divs').nth(1)).toHaveClass(/foo/);\n\t\tawait expect(find('.divs').nth(2)).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can target another div",
    "html": "<div id='bar'></div><div _='on click add .foo to #bar'></div>",
    "body": "\n\t\tawait html(\"<div id='bar'></div><div _='on click add .foo to #bar'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo/);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "can wait",
    "html": "<div _='on click                              add .foo then                              wait 20ms then                              add .bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait 20ms then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "cleanup clears elt._hyperscript",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tvar hasState = await evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\t_hyperscript.cleanup(div);\n\t\t\treturn '_hyperscript' in div;\n\t\t});\n\t\texpect(hasState).toBe(false);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "cleanup removes cross-element event listeners",
    "html": "<div id='source'></div><div id='target' _='on click from #source add .foo'></div>",
    "body": "\n\t\tawait html(\"<div id='source'></div><div id='target' _='on click from #source add .foo'></div>\");\n\n\t\t// Verify the cross-element listener works\n\t\tawait find('#source').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveClass(/foo/);\n\n\t\t// Cleanup target and verify listener on source is removed\n\t\tvar listenerRemoved = await evaluate(() => {\n\t\t\tvar source = document.getElementById('source');\n\t\t\tvar target = document.getElementById('target');\n\t\t\t_hyperscript.cleanup(target);\n\t\t\ttarget.classList.remove('foo');\n\t\t\tsource.click();\n\t\t\t// If cleanup worked, target should NOT get .foo again\n\t\t\treturn !target.classList.contains('foo');\n\t\t});\n\t\texpect(listenerRemoved).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "cleanup removes data-hyperscript-powered",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('data-hyperscript-powered', 'true');\n\t\tawait evaluate(() => {\n\t\t\t_hyperscript.cleanup(document.querySelector('#work-area div'));\n\t\t});\n\t\texpect(await find('div').getAttribute('data-hyperscript-powered')).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "cleanup removes event listeners on the element",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\n\t\t// Cleanup and verify listener is gone\n\t\tawait evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\t_hyperscript.cleanup(div);\n\t\t\tdiv.classList.remove('foo');\n\t\t\tdiv.click();\n\t\t});\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "cleanup tracks listeners in elt._hyperscript",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tvar info = await evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\treturn {\n\t\t\t\thasListeners: Array.isArray(div._hyperscript.listeners),\n\t\t\t\tlistenerCount: div._hyperscript.listeners.length,\n\t\t\t};\n\t\t});\n\t\texpect(info.hasListeners).toBe(true);\n\t\texpect(info.listenerCount).toBeGreaterThan(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "fires hyperscript:before:cleanup and hyperscript:after:cleanup",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tvar events = await evaluate(() => {\n\t\t\tvar events = [];\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\tdiv.addEventListener('hyperscript:before:cleanup', () => events.push('before:cleanup'));\n\t\t\tdiv.addEventListener('hyperscript:after:cleanup', () => events.push('after:cleanup'));\n\t\t\t_hyperscript.cleanup(div);\n\t\t\treturn events;\n\t\t});\n\t\texpect(events).toEqual(['before:cleanup', 'after:cleanup']);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "fires hyperscript:before:init and hyperscript:after:init",
    "html": "",
    "body": "\n\t\tvar events = await evaluate(() => {\n\t\t\tvar events = [];\n\t\t\tvar wa = document.getElementById('work-area');\n\t\t\twa.addEventListener('hyperscript:before:init', () => events.push('before:init'));\n\t\t\twa.addEventListener('hyperscript:after:init', () => events.push('after:init'));\n\t\t\twa.innerHTML = \"<div _='on click add .foo'></div>\";\n\t\t\t_hyperscript.processNode(wa);\n\t\t\treturn events;\n\t\t});\n\t\texpect(events).toEqual(['before:init', 'after:init']);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/bootstrap",
    "name": "hyperscript can have more than one action",
    "html": "<div id='bar'></div><div _='on click                              add .foo to #bar then                              add .blah'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='bar'></div>\" +\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo to #bar then \" +\n\t\t\t\t\"                             add .blah'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo/);\n\t\tawait expect(find('#bar')).not.toHaveClass(/blah/);\n\t\tawait expect(find('div:nth-of-type(2)')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div:nth-of-type(2)')).toHaveClass(/blah/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "hyperscript:before:init can cancel initialization",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => {\n\t\t\tvar wa = document.getElementById('work-area');\n\t\t\twa.addEventListener('hyperscript:before:init', (e) => e.preventDefault(), { once: true });\n\t\t\twa.innerHTML = \"<div _='on click add .foo'></div>\";\n\t\t\t_hyperscript.processNode(wa);\n\t\t\tvar div = wa.querySelector('div');\n\t\t\treturn {\n\t\t\t\tinitialized: !!div._hyperscript?.initialized,\n\t\t\t\thasPowered: div.hasAttribute('data-hyperscript-powered'),\n\t\t\t};\n\t\t});\n\t\texpect(result.initialized).toBe(false);\n\t\texpect(result.hasPowered).toBe(false);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/bootstrap",
    "name": "logAll config logs events to console",
    "html": "",
    "body": "\n\t\tvar logged = await evaluate(() => {\n\t\t\tvar logs = [];\n\t\t\tvar origLog = console.log;\n\t\t\tconsole.log = (...args) => logs.push(args[0]);\n\t\t\t_hyperscript.config.logAll = true;\n\t\t\ttry {\n\t\t\t\tvar wa = document.getElementById('work-area');\n\t\t\t\twa.innerHTML = \"<div _='on click add .foo'></div>\";\n\t\t\t\t_hyperscript.processNode(wa);\n\t\t\t} finally {\n\t\t\t\t_hyperscript.config.logAll = false;\n\t\t\t\tconsole.log = origLog;\n\t\t\t}\n\t\t\treturn logs.some(l => typeof l === 'string' && l.includes('hyperscript:'));\n\t\t});\n\t\texpect(logged).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/bootstrap",
    "name": "on a single div",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "reinitializes if script attribute changes",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\n\t\t// Change the script and reprocess (simulates morph swap)\n\t\tawait evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\tdiv.setAttribute('_', 'on click add .bar');\n\t\t\t_hyperscript.processNode(div);\n\t\t});\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "sets data-hyperscript-powered on initialized elements",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('data-hyperscript-powered', 'true');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "skips reinitialization if script unchanged",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\t// Process again - should be a no-op\n\t\tvar clickCount = await evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\tvar count = 0;\n\t\t\tdiv.addEventListener('click', () => count++);\n\t\t\t_hyperscript.processNode(div);\n\t\t\tdiv.click();\n\t\t\treturn count;\n\t\t});\n\t\t// Only 1 click handler should fire (the original), not 2\n\t\texpect(clickCount).toBe(1);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "stores state on elt._hyperscript",
    "html": "<div _='on click add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click add .foo'></div>\");\n\t\tvar state = await evaluate(() => {\n\t\t\tvar div = document.querySelector('#work-area div');\n\t\t\treturn {\n\t\t\t\thasProperty: '_hyperscript' in div,\n\t\t\t\tinitialized: div._hyperscript?.initialized,\n\t\t\t\thasScriptHash: typeof div._hyperscript?.scriptHash === 'number',\n\t\t\t};\n\t\t});\n\t\texpect(state.hasProperty).toBe(true);\n\t\texpect(state.initialized).toBe(true);\n\t\texpect(state.hasScriptHash).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/bootstrap",
    "name": "toggles",
    "html": "<div _='on click toggle .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/dom-scope",
    "name": "closest jumps to matching ancestor",
    "html": "\n\t\t\t<div class=\"outer\" _=\"init set ^val to 'from-outer'\">\n\t\t\t\t<div dom-scope=\"isolated\" _=\"init set ^val to 'from-inner'\">\n\t\t\t\t\t<span dom-scope=\"closest .outer\" _=\"init put ^val into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div class=\"outer\" _=\"init set ^val to 'from-outer'\">\n\t\t\t\t<div dom-scope=\"isolated\" _=\"init set ^val to 'from-inner'\">\n\t\t\t\t\t<span dom-scope=\"closest .outer\" _=\"init put ^val into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('from-outer')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/dom-scope",
    "name": "closest with no match stops resolution",
    "html": "\n\t\t\t<div _=\"init set ^val to 'found'\">\n\t\t\t\t<span dom-scope=\"closest .nonexistent\" _=\"init if ^val is not undefined put 'leaked' into me else put 'blocked' into me\">waiting</span>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^val to 'found'\">\n\t\t\t\t<span dom-scope=\"closest .nonexistent\" _=\"init if ^val is not undefined put 'leaked' into me else put 'blocked' into me\">waiting</span>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('blocked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/dom-scope",
    "name": "isolated allows setting ^var on the isolated element itself",
    "html": "\n\t\t\t<div _=\"init set ^outer to 'leaked'\">\n\t\t\t\t<div dom-scope=\"isolated\" _=\"init set ^inner to 'contained'\">\n\t\t\t\t\t<span _=\"init put ^inner into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^outer to 'leaked'\">\n\t\t\t\t<div dom-scope=\"isolated\" _=\"init set ^inner to 'contained'\">\n\t\t\t\t\t<span _=\"init put ^inner into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('contained')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/dom-scope",
    "name": "isolated stops ^var resolution",
    "html": "\n\t\t\t<div _=\"init set ^color to 'red'\">\n\t\t\t\t<div dom-scope=\"isolated\">\n\t\t\t\t\t<span _=\"init if ^color is not undefined put 'leaked' into me else put 'blocked' into me\">waiting</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^color to 'red'\">\n\t\t\t\t<div dom-scope=\"isolated\">\n\t\t\t\t\t<span _=\"init if ^color is not undefined put 'leaked' into me else put 'blocked' into me\">waiting</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('blocked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/dom-scope",
    "name": "parent of jumps past matching ancestor to its parent",
    "html": "\n\t\t\t<div class=\"outer\" _=\"init set ^val to 'from-outer'\">\n\t\t\t\t<div class=\"middle\" dom-scope=\"isolated\" _=\"init set ^val to 'from-middle'\">\n\t\t\t\t\t<span dom-scope=\"parent of .middle\" _=\"init put ^val into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div class=\"outer\" _=\"init set ^val to 'from-outer'\">\n\t\t\t\t<div class=\"middle\" dom-scope=\"isolated\" _=\"init set ^val to 'from-middle'\">\n\t\t\t\t\t<span dom-scope=\"parent of .middle\" _=\"init put ^val into me\">none</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('from-outer')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/evalStatically",
    "name": "throws on math expressions",
    "html": "",
    "body": "\n\t\tvar msg = await evaluate(() => {\n\t\t\ttry { _hyperscript.parse(\"1 + 2\").evalStatically(); return null; }\n\t\t\tcatch (e) { return e.message; }\n\t\t});\n\t\texpect(msg).toMatch(/cannot be evaluated statically/);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "throws on symbol references",
    "html": "",
    "body": "\n\t\tvar msg = await evaluate(() => {\n\t\t\ttry { _hyperscript.parse(\"x\").evalStatically(); return null; }\n\t\t\tcatch (e) { return e.message; }\n\t\t});\n\t\texpect(msg).toMatch(/cannot be evaluated statically/);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "throws on template strings",
    "html": "",
    "body": "\n\t\tvar msg = await evaluate(() => {\n\t\t\ttry { _hyperscript.parse('`hello ${name}`').evalStatically(); return null; }\n\t\t\tcatch (e) { return e.message; }\n\t\t});\n\t\texpect(msg).toMatch(/cannot be evaluated statically/);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "works on boolean literals",
    "html": "",
    "body": "\n\t\texpect(await evaluate(() => _hyperscript.parse(\"true\").evalStatically())).toBe(true);\n\t\texpect(await evaluate(() => _hyperscript.parse(\"false\").evalStatically())).toBe(false);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "works on null literal",
    "html": "",
    "body": "\n\t\texpect(await evaluate(() => _hyperscript.parse(\"null\").evalStatically())).toBe(null);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "works on number literals",
    "html": "",
    "body": "\n\t\texpect(await evaluate(() => _hyperscript.parse(\"42\").evalStatically())).toBe(42);\n\t\texpect(await evaluate(() => _hyperscript.parse(\"3.14\").evalStatically())).toBe(3.14);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "works on plain string literals",
    "html": "",
    "body": "\n\t\texpect(await evaluate(() => _hyperscript.parse('\"hello\"').evalStatically())).toBe(\"hello\");\n\t\texpect(await evaluate(() => _hyperscript.parse(\"'world'\").evalStatically())).toBe(\"world\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/evalStatically",
    "name": "works on time expressions",
    "html": "",
    "body": "\n\t\texpect(await evaluate(() => _hyperscript.parse(\"200ms\").evalStatically())).toBe(200);\n\t\texpect(await evaluate(() => _hyperscript.parse(\"2s\").evalStatically())).toBe(2000);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/liveTemplate",
    "name": "applies init script from _ attribute",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^msg to 'initialized'\">\n\t\t\t\t<span>${}{^msg}</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^msg to 'initialized'\">\n\t\t\t\t<span>${\"\\x24\"}{^msg}</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('initialized')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "loop index variable is captured alongside loop variable",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $idxItems index i\n\t\t\t\t\t<li _=\"on click put i + ':' + item into me\">${}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $idxItems to ['A','B','C']\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $idxItems index i\n\t\t\t\t\t<li _=\"on click put i + ':' + item into me\">${\"\\x24\"}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(3)\n\t\tawait find('[data-live-template] li').nth(1).click()\n\t\tawait expect(find('[data-live-template] li').nth(1)).toHaveText('1:B')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "loop variable capture works with remove for live list",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $removeItems\n\t\t\t\t\t<li><span>${}{item.name}</span><button _=\"on click remove item from $removeItems\">x</button></li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $removeItems to [{name:'A'},{name:'B'},{name:'C'}]\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $removeItems\n\t\t\t\t\t<li><span>${\"\\x24\"}{item.name}</span><button _=\"on click remove item from $removeItems\">x</button></li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(3)\n\t\t// Remove the middle item \"B\"\n\t\tawait find('[data-live-template] li').nth(1).locator('button').click()\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(2)\n\t\tawait expect(find('[data-live-template] li').first().locator('span')).toHaveText('A')\n\t\tawait expect(find('[data-live-template] li').last().locator('span')).toHaveText('C')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "loop variables are captured and available in _= handlers",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $captureItems index i\n\t\t\t\t\t<li _=\"on click put item.name into me\">${}{item.name}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $captureItems to [{name:'Alice'},{name:'Bob'},{name:'Charlie'}]\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $captureItems index i\n\t\t\t\t\t<li _=\"on click put item.name into me\">${\"\\x24\"}{item.name}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(3)\n\t\tawait find('[data-live-template] li').nth(1).click()\n\t\tawait expect(find('[data-live-template] li').nth(1)).toHaveText('Bob')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "multiple live templates are independent",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^x to 'first'\">\n\t\t\t\t<span class=\"a\">${}{^x}</span>\n\t\t\t</script>\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^x to 'second'\">\n\t\t\t\t<span class=\"b\">${}{^x}</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^x to 'first'\">\n\t\t\t\t<span class=\"a\">${\"\\x24\"}{^x}</span>\n\t\t\t</script>\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^x to 'second'\">\n\t\t\t\t<span class=\"b\">${\"\\x24\"}{^x}</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] .a').textContent()).toBe('first')\n\t\tawait expect.poll(() => find('[data-live-template] .b').textContent()).toBe('second')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "processes hyperscript on inner elements",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^val to 0\">\n\t\t\t\t<button _=\"on click increment ^val then put ^val into the next <output/>\">+</button>\n\t\t\t\t<output>0</output>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^val to 0\">\n\t\t\t\t<button _=\"on click increment ^val then put ^val into the next <output/>\">+</button>\n\t\t\t\t<output>0</output>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] output').textContent()).toBe('0')\n\t\tawait find('[data-live-template] button').click()\n\t\tawait expect.poll(() => find('[data-live-template] output').textContent()).toBe('1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "reactively updates when dependencies change",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t\t<span>Count: ${}{^count}</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t\t<span>Count: ${\"\\x24\"}{^count}</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('Count: 0')\n\t\tawait find('[data-live-template] button').click()\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('Count: 1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "reacts to global state without init script",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<p>Hello, ${}{$ltGlobal}!</p>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $ltGlobal to 'World'\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<p>Hello, ${\"\\x24\"}{$ltGlobal}!</p>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] p').textContent()).toBe('Hello, World!')\n\t\tawait run(\"set $ltGlobal to 'Carson'\")\n\t\tawait expect.poll(() => find('[data-live-template] p').textContent()).toBe('Hello, Carson!')\n\t\tawait evaluate(() => { delete window.$ltGlobal })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "renders static content after the template",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>Hello World</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>Hello World</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('Hello World')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "renders template expressions",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>Hello ${}{$ltName}!</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $ltName to 'hyperscript'\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>Hello ${\"\\x24\"}{$ltName}!</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('Hello hyperscript!')\n\t\tawait evaluate(() => { delete window.$ltName })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "scope is refreshed after morph so surviving elements get updated indices",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $morphItems index i\n\t\t\t\t\t<li _=\"on click put i + ':' + item.name into me\">${}{item.name}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait run(\"set $morphItems to [{name:'A'},{name:'B'},{name:'C'}]\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<ul>\n\t\t\t\t#for item in $morphItems index i\n\t\t\t\t\t<li _=\"on click put i + ':' + item.name into me\">${\"\\x24\"}{item.name}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(3)\n\t\t// Verify initial scope: clicking C should show \"2:C\"\n\t\tawait find('[data-live-template] li').last().click()\n\t\tawait expect(find('[data-live-template] li').last()).toHaveText('2:C')\n\t\t// Remove B — C shifts from index 2 to index 1\n\t\tawait run(\"call $morphItems.splice(1, 1)\")\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(2)\n\t\t// After morph, C's scope should be refreshed: now \"1:C\"\n\t\tawait find('[data-live-template] li').last().click()\n\t\tawait expect(find('[data-live-template] li').last()).toHaveText('1:C')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "script type=\"text/hyperscript-template\" works as a live template source",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^stMsg to 'from script'\">\n\t\t\t\t<span>${}{^stMsg}</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^stMsg to 'from script'\">\n\t\t\t\t<span>${\"\\x24\"}{^stMsg}</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('from script')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "script-based live template preserves ${} in bare attribute position",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^items to [{text:'A', done:true},{text:'B', done:false}]\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li class=\"${}{'done' if item.done}\" data-text=\"${}{item.text}\">\n\t\t\t\t\t\t<input type=\"checkbox\" ${}{'checked' if item.done}>\n\t\t\t\t\t\t<span>${}{item.text}</span>\n\t\t\t\t\t</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^items to [{text:'A', done:true},{text:'B', done:false}]\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li class=\"${\"\\x24\"}{'done' if item.done}\" data-text=\"${\"\\x24\"}{item.text}\">\n\t\t\t\t\t\t<input type=\"checkbox\" ${\"\\x24\"}{'checked' if item.done}>\n\t\t\t\t\t\t<span>${\"\\x24\"}{item.text}</span>\n\t\t\t\t\t</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(2)\n\t\tvar firstChecked = await page.evaluate(() =>\n\t\t\tdocument.querySelector('[data-live-template] li:first-of-type input').checked)\n\t\texpect(firstChecked).toBe(true)\n\t\tvar secondChecked = await page.evaluate(() =>\n\t\t\tdocument.querySelector('[data-live-template] li:last-of-type input').checked)\n\t\texpect(secondChecked).toBe(false)\n\t\tawait expect(find('[data-live-template] li').first()).toHaveClass(/done/)\n\t\tawait expect(find('[data-live-template] li').last()).not.toHaveClass(/done/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "supports #for loops",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^items to ['a', 'b', 'c']\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li>${}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^items to ['a', 'b', 'c']\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li>${\"\\x24\"}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] li').count()).toBe(3)\n\t\tawait expect.poll(() => find('[data-live-template] li').first().textContent()).toBe('a')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "supports #if conditionals",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^show to true\">\n\t\t\t\t#if ^show\n\t\t\t\t\t<span>visible</span>\n\t\t\t\t#end\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live _=\"init set ^show to true\">\n\t\t\t\t#if ^show\n\t\t\t\t\t<span>visible</span>\n\t\t\t\t#end\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('visible')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/liveTemplate",
    "name": "wrapper has display:contents",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>test</span>\n\t\t\t</script>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" live>\n\t\t\t\t<span>test</span>\n\t\t\t</script>\n\t\t`)\n\t\tawait expect.poll(() => find('[data-live-template] span').textContent()).toBe('test')\n\t\tvar display = await evaluate(() =>\n\t\t\tdocument.querySelector('[data-live-template]').style.display\n\t\t)\n\t\texpect(display).toBe('contents')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "_hyperscript() evaluate API still throws on first error",
    "html": "",
    "body": "\n\t\tvar msg = await error(\"add - to\");\n\t\texpect(msg).toMatch(/^Expected either a class reference or attribute expression/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "basic parse error messages work",
    "html": "",
    "body": "\n\t\tvar msg = await error(\"add - to\");\n\t\texpect(msg).toMatch(/^Expected either a class reference or attribute expression/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "can have alternate comments in attributes",
    "html": "<div _='on click put \"clicked\" into my.innerHTML // put some content into the div...'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click put \\\"clicked\\\" into my.innerHTML // put some content into the div...'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "can have alternate comments in scripts",
    "html": "<script type='text/hyperscript'>// this is a comment\ndef foo() // this is another comment\n  return \"foo\"\nend // end with a comment</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"// this is a comment\\n\" +\n\t\t\t\t\"def foo() // this is another comment\\n\" +\n\t\t\t\t'  return \"foo\"\\n' +\n\t\t\t\t\"end // end with a comment\" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\texpect(await evaluate(() => foo())).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/parser",
    "name": "can have comments in attributes",
    "html": "<div _='on click put \"clicked\" into my.innerHTML -- put some content into the div...'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click put \\\"clicked\\\" into my.innerHTML -- put some content into the div...'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "can have comments in attributes (triple dash)",
    "html": "<div _='on click put \"clicked\" into my.innerHTML ---put some content into the div...'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click put \\\"clicked\\\" into my.innerHTML ---put some content into the div...'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "can have comments in scripts",
    "html": "<script type='text/hyperscript'>-- this is a comment\ndef foo() -- this is another comment\n  return \"foo\"\nend -- end with a comment--- this is a comment\n----this is a comment----\ndef bar() ---this is another comment\n  return \"bar\"\nend --- end with a comment</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"-- this is a comment\\n\" +\n\t\t\t\t\"def foo() -- this is another comment\\n\" +\n\t\t\t\t'  return \"foo\"\\n' +\n\t\t\t\t\"end -- end with a comment\" +\n\t\t\t\t\"--- this is a comment\\n\" +\n\t\t\t\t\"----this is a comment----\\n\" +\n\t\t\t\t\"def bar() ---this is another comment\\n\" +\n\t\t\t\t'  return \"bar\"\\n' +\n\t\t\t\t\"end --- end with a comment\" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\texpect(await evaluate(() => foo())).toBe(\"foo\");\n\t\texpect(await evaluate(() => bar())).toBe(\"bar\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/parser",
    "name": "can support parenthesized commands and features",
    "html": "<div _='(on click (log me) (trigger foo))                (on foo (put \"clicked\" into my.innerHTML))'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='(on click (log me) (trigger foo))\" +\n\t\t\t\t'                (on foo (put \"clicked\" into my.innerHTML))\\'></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "continues initializing elements in the presence of a parse error",
    "html": "<div><div id='d1' _='on click bad'></div><div id='d2' _='on click put \"clicked\" into my.innerHTML'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div>\" +\n\t\t\t\t\"<div id='d1' _='on click bad'></div>\" +\n\t\t\t\t\"<div id='d2' _='on click put \\\"clicked\\\" into my.innerHTML'></div>\" +\n\t\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "element-level isolation still works with error recovery",
    "html": "<div><div id='d1' _='on click blargh end on mouseenter also_bad'></div><div id='d2' _='on click put \"clicked\" into my.innerHTML'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div>\" +\n\t\t\t\t\"<div id='d1' _='on click blargh end on mouseenter also_bad'></div>\" +\n\t\t\t\t\"<div id='d2' _='on click put \\\"clicked\\\" into my.innerHTML'></div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "fires hyperscript:parse-error event with all errors",
    "html": "",
    "body": "\n\t\tvar errorCount = await evaluate(() => {\n\t\t\tvar div = document.createElement('div');\n\t\t\tdiv.setAttribute('_', 'on click blargh end on mouseenter also_bad');\n\t\t\tvar count = 0;\n\t\t\tdiv.addEventListener('hyperscript:parse-error', (e) => {\n\t\t\t\tcount = e.detail.errors.length;\n\t\t\t});\n\t\t\tdocument.body.appendChild(div);\n\t\t\t_hyperscript.processNode(div);\n\t\t\treturn count;\n\t\t});\n\t\texpect(errorCount).toBe(2);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/parser",
    "name": "parse error at EOF on trailing newline does not crash",
    "html": "",
    "body": "\n\t\t// source ending with \\n means last line is empty; EOF token has no .line\n\t\tvar result = await evaluate((src) => {\n\t\t\ttry {\n\t\t\t\t_hyperscript(src);\n\t\t\t\treturn \"no-error\";\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof RangeError) return \"RangeError: \" + e.message;\n\t\t\t\treturn \"ok: \" + typeof e.message;\n\t\t\t}\n\t\t}, \"set x to\\n\");\n\t\texpect(result).toMatch(/^ok:/);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/parser",
    "name": "recovers across feature boundaries and reports all errors",
    "html": "<div id='d1' _='on click blargh end on mouseenter put \"hovered\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click blargh end on mouseenter put \\\"hovered\\\" into my.innerHTML'></div>\"\n\t\t);\n\t\t// Element should not execute at all (no features installed)\n\t\tvar powered = await evaluate(() => document.querySelector('#d1').hasAttribute('data-hyperscript-powered'));\n\t\texpect(powered).toBe(false);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/parser",
    "name": "recovers across multiple feature errors",
    "html": "<div id='d1' _='on click blargh end on mouseenter also_bad end on focus put \"focused\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click blargh end on mouseenter also_bad end on focus put \\\"focused\\\" into my.innerHTML'></div>\"\n\t\t);\n\t\t// Element should not execute - errors prevent all features\n\t\tvar powered = await evaluate(() => document.querySelector('#d1').hasAttribute('data-hyperscript-powered'));\n\t\texpect(powered).toBe(false);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/reactivity",
    "name": "NaN → NaN does not retrigger handlers (Object.is semantics)",
    "html": "<div _=\"when $rxNanVal changes increment $rxNanCount\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.$rxNanCount = 0; window.$rxNanVal = NaN })\n\t\tawait html(`<div _=\"when $rxNanVal changes increment $rxNanCount\"></div>`)\n\t\t// Initial evaluate should not fire handler because NaN is \"null-ish\" in _lastValue init?\n\t\t// It actually DOES fire (initialize sees non-null). Snapshot and compare.\n\t\tvar initial = await evaluate(() => window.$rxNanCount)\n\n\t\tawait run(\"set $rxNanVal to NaN\")\n\t\t// Give the microtask a chance to run\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 20)))\n\t\texpect(await evaluate(() => window.$rxNanCount)).toBe(initial)\n\n\t\t// But changing to a real number should fire\n\t\tawait run(\"set $rxNanVal to 42\")\n\t\tawait expect.poll(() => evaluate(() => window.$rxNanCount)).toBe(initial + 1)\n\n\t\tawait evaluate(() => { delete window.$rxNanCount; delete window.$rxNanVal })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/reactivity",
    "name": "effect switches its dependencies based on control flow",
    "html": "<div _=\"live if $rxCond put $rxA into me else put $rxB into me end end\"></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.$rxCond = true\n\t\t\twindow.$rxA = 'from-a'\n\t\t\twindow.$rxB = 'from-b'\n\t\t})\n\t\tawait html(\n\t\t\t`<div _=\"live if $rxCond put $rxA into me else put $rxB into me end end\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveText('from-a')\n\n\t\t// While cond is true, changing $rxB should NOT retrigger\n\t\tawait run(\"set $rxB to 'ignored'\")\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 20)))\n\t\tawait expect(find('div')).toHaveText('from-a')\n\n\t\t// Switch cond → effect now depends on $rxB\n\t\tawait run(\"set $rxCond to false\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('ignored')\n\n\t\t// Now $rxA changes should be ignored, $rxB changes should fire\n\t\tawait run(\"set $rxA to 'a-ignored'\")\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 20)))\n\t\tawait expect(find('div')).toHaveText('ignored')\n\n\t\tawait run(\"set $rxB to 'new-b'\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('new-b')\n\n\t\tawait evaluate(() => {\n\t\t\tdelete window.$rxCond; delete window.$rxA; delete window.$rxB\n\t\t})\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/reactivity",
    "name": "effects fire in source registration order",
    "html": "<div _=\"when $rxTrigger changes call $rxOrder.push('first')\"></div><div _=\"when $rxTrigger changes call $rxOrder.push('second')\"></div><div _=\"when $rxTrigger changes call $rxOrder.push('third')\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.$rxOrder = []; window.$rxTrigger = 0 })\n\t\tawait html(\n\t\t\t`<div _=\"when $rxTrigger changes call $rxOrder.push('first')\"></div>` +\n\t\t\t`<div _=\"when $rxTrigger changes call $rxOrder.push('second')\"></div>` +\n\t\t\t`<div _=\"when $rxTrigger changes call $rxOrder.push('third')\"></div>`\n\t\t)\n\t\t// Clear initial-subscribe firings so we see only the update ordering\n\t\tawait evaluate(() => { window.$rxOrder.length = 0 })\n\n\t\tawait run(\"set $rxTrigger to 1\")\n\t\tawait expect.poll(() => evaluate(() => window.$rxOrder.length)).toBe(3)\n\t\texpect(await evaluate(() => window.$rxOrder.slice())).toEqual(['first', 'second', 'third'])\n\n\t\tawait evaluate(() => { delete window.$rxOrder; delete window.$rxTrigger })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/reactivity",
    "name": "effects on disconnected elements stop automatically",
    "html": "<div id=\"persist\" _=\"when $rxDcVal changes increment $rxDcCount then put $rxDcVal into me\"></div><div id=\"doomed\" _=\"when $rxDcVal changes increment $rxDcCount\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.$rxDcVal = 'x'; window.$rxDcCount = 0 })\n\t\tawait html(\n\t\t\t`<div id=\"persist\" _=\"when $rxDcVal changes increment $rxDcCount then put $rxDcVal into me\"></div>` +\n\t\t\t`<div id=\"doomed\" _=\"when $rxDcVal changes increment $rxDcCount\"></div>`\n\t\t)\n\t\tawait expect(find('#persist')).toHaveText('x')\n\t\tvar initialCount = await evaluate(() => window.$rxDcCount)\n\n\t\t// Remove one element\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area #doomed').remove()\n\t\t})\n\n\t\t// Trigger a change - only the surviving effect should run\n\t\tawait run(\"set $rxDcVal to 'y'\")\n\t\tawait expect(find('#persist')).toHaveText('y')\n\n\t\t// Count advanced by exactly 1 (the surviving handler), not 2\n\t\texpect(await evaluate(() => window.$rxDcCount)).toBe(initialCount + 1)\n\n\t\tawait evaluate(() => { delete window.$rxDcVal; delete window.$rxDcCount })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/reactivity",
    "name": "element-scoped writes only trigger effects on the same element",
    "html": "<div id=\"a\" _=\"init set :count to 0\n\t\t\t                on click increment :count\n\t\t\t                when :count changes put :count into me\"></div><div id=\"b\" _=\"init set :count to 0\n\t\t\t                when :count changes put :count into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"a\" _=\"init set :count to 0\n\t\t\t                on click increment :count\n\t\t\t                when :count changes put :count into me\"></div>` +\n\t\t\t`<div id=\"b\" _=\"init set :count to 0\n\t\t\t                when :count changes put :count into me\"></div>`\n\t\t)\n\t\tawait expect(find('#a')).toHaveText('0')\n\t\tawait expect(find('#b')).toHaveText('0')\n\n\t\t// Click a - only #a updates\n\t\tawait find('#a').dispatchEvent('click')\n\t\tawait expect(find('#a')).toHaveText('1')\n\t\t// b's count is untouched - its effect did not run\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 20)))\n\t\tawait expect(find('#b')).toHaveText('0')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/reactivity",
    "name": "multiple effects on the same global fire once per write",
    "html": "<div id=\"a\" _=\"when $rxVal changes increment $rxCount then put $rxVal into me\"></div><div id=\"b\" _=\"when $rxVal changes put $rxVal into me\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.$rxCount = 0; window.$rxVal = 'a' })\n\t\tawait html(\n\t\t\t`<div id=\"a\" _=\"when $rxVal changes increment $rxCount then put $rxVal into me\"></div>` +\n\t\t\t`<div id=\"b\" _=\"when $rxVal changes put $rxVal into me\"></div>`\n\t\t)\n\t\tawait expect(find('#a')).toHaveText('a')\n\t\tawait expect(find('#b')).toHaveText('a')\n\t\t// Snapshot after initial subscribe cascade settles\n\t\tvar initialCount = await evaluate(() => window.$rxCount)\n\n\t\tawait run(\"set $rxVal to 'b'\")\n\t\tawait expect(find('#a')).toHaveText('b')\n\t\tawait expect(find('#b')).toHaveText('b')\n\n\t\t// Exactly one invocation of the counting effect for the single write\n\t\texpect(await evaluate(() => window.$rxCount)).toBe(initialCount + 1)\n\t\tawait evaluate(() => { delete window.$rxCount; delete window.$rxVal })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/reactivity",
    "name": "reactive loops are detected and stopped after 100 consecutive triggers",
    "html": "<div _=\"when $rxLoop changes increment $rxLoop\"></div>",
    "body": "\n\t\tvar errors = []\n\t\tpage.on('console', m => { if (m.type() === 'error') errors.push(m.text()) })\n\n\t\tawait evaluate(() => { window.$rxLoop = 0 })\n\t\t// Handler mutates the variable it watches - cascade must be bounded\n\t\tawait html(`<div _=\"when $rxLoop changes increment $rxLoop\"></div>`)\n\n\t\tawait run(\"set $rxLoop to 1\")\n\t\t// Wait for the cascade to burn itself out\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 100)))\n\n\t\tvar finalVal = await evaluate(() => window.$rxLoop)\n\t\t// Loop guard kicks in at 100 consecutive triggers.\n\t\t// Not asserting exact value because the initial subscribe run + the\n\t\t// `set $rxLoop to 1` trigger contribute variable increments before\n\t\t// the guard trips. Just verify it stopped in a bounded way.\n\t\texpect(finalVal).toBeGreaterThan(100)\n\t\texpect(finalVal).toBeLessThan(200)\n\n\t\t// And the error was reported\n\t\texpect(errors.some(e => /Reactivity loop/.test(e))).toBe(true)\n\n\t\tawait evaluate(() => { delete window.$rxLoop })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/reactivity",
    "name": "setting same value does not retrigger handler",
    "html": "<div _=\"when $rxSameVal changes increment $rxSameCount\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.$rxSameCount = 0; window.$rxSameVal = 5 })\n\t\tawait html(`<div _=\"when $rxSameVal changes increment $rxSameCount\"></div>`)\n\t\tvar initial = await evaluate(() => window.$rxSameCount)\n\n\t\tawait run(\"set $rxSameVal to 5\")\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 20)))\n\t\texpect(await evaluate(() => window.$rxSameCount)).toBe(initial)\n\n\t\tawait evaluate(() => { delete window.$rxSameCount; delete window.$rxSameVal })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "core/regressions",
    "name": "async exception",
    "html": "<div _='on click async transition opacity to 0 log \"hello!\"'></div>",
    "body": "\n\t\tawait html(\"<div _='on click async transition opacity to 0 log \\\"hello!\\\"'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "button query in form",
    "html": "<form _='on click get the <button/> in me then                                   set it @disabled to true'>  <button id='b1'>Button</button></form>",
    "body": "\n\t\tawait html(\"<form _='on click get the <button/> in me then\" +\n\t\t\t\"                                   set it @disabled to true'>\" +\n\t\t\t\"  <button id='b1'>Button</button>\" +\n\t\t\t\"</form>\");\n\t\tawait find('form').dispatchEvent('click');\n\t\tconst disabled = await evaluate(() => document.querySelector('#b1').disabled);\n\t\texpect(disabled).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can create a paragraph tag",
    "html": "<input id='i1' value='foo'><div id='d2'></div><div _='on click make a <p/>  put #i1.value into its textContent put it.outerHTML at end of #d2'></div>",
    "body": "\n\t\tawait html(\"<input id='i1' value='foo'>\" +\n\t\t\t\"<div id='d2'></div>\" +\n\t\t\t\"<div _='on click make a <p/>  put #i1.value into its textContent put it.outerHTML at end of #d2'></div>\");\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toContainText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can invoke functions w/ numbers in name",
    "html": "<button _='on click put select2() into me'/>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.select2 = function(){ return \"select2\"; };\n\t\t});\n\t\tawait html(\"<button _='on click put select2() into me'/>\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('button')).toHaveText(\"select2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can pick detail fields out by name",
    "html": "<div id='d1'></div><input debug='true' _='on onchange if my.value !== \"\" then trigger customEvt end end on customEvt log event then put my.value into #d1.innerHTML'/>",
    "body": "\n\t\tawait html(\"<div id='d1'></div>\" +\n\t\t\t\"<input debug='true' _='on onchange if my.value !== \\\"\\\" then trigger customEvt end end \" +\n\t\t\t\t\"on customEvt log event then put my.value into #d1.innerHTML'/>\"\n\t\t);\n\t\tawait evaluate(() => {\n\t\t\tconst input = document.querySelector('#work-area input');\n\t\t\tinput.value = \"foo\";\n\t\t\tinput.dispatchEvent(new Event(\"onchange\"));\n\t\t\tinput.value = \"\";\n\t\t\tinput.dispatchEvent(new Event(\"onchange\"));\n\t\t});\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t\tawait evaluate(() => {\n\t\t\tconst input = document.querySelector('#work-area input');\n\t\t\tinput.value = \"bar\";\n\t\t\tinput.dispatchEvent(new Event(\"onchange\"));\n\t\t});\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can refer to function in init blocks",
    "html": "<script type='text/hyperscript'>  init     call foo()   end   def foo()     put \"here\" into #d1's innerHTML   end</script> <div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"  init \" +\n\t\t\t\"    call foo() \" +\n\t\t\t\"  end \" +\n\t\t\t\"  def foo() \" +\n\t\t\t\"    put \\\"here\\\" into #d1's innerHTML \" +\n\t\t\t\"  end</script> \" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t);\n\t\tawait expect(find('#d1')).toHaveText(\"here\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/regressions",
    "name": "can remove by clicks elsewhere",
    "html": "<div id='target' _='on click elsewhere remove me'></div><div id='other'></div>",
    "body": "\n\t\tawait html(\"<div id='target' _='on click elsewhere remove me'></div><div id='other'></div>\");\n\t\tawait evaluate(() => document.querySelector('#other').click());\n\t\tawait expect(find('#target')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can remove class by id",
    "html": "<form class='hideme' id='email-form'></form><div _='on click remove .hideme from #email-form'></div>",
    "body": "\n\t\tawait html(\"<form class='hideme' id='email-form'></form>\" +\n\t\t\t\"<div _='on click remove .hideme from #email-form'></div>\");\n\t\tawait expect(find('#email-form')).toHaveClass(/hideme/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('#email-form')).not.toHaveClass(/hideme/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "can trigger htmx events",
    "html": "<div id='div1' _='on htmx:foo put \"foo\" into my.innerHTML'></div><div _='on click send htmx:foo to #div1'></div>",
    "body": "\n\t\tawait html(\"<div id='div1' _='on htmx:foo put \\\"foo\\\" into my.innerHTML'></div>\" +\n\t\t\t\"<div _='on click send htmx:foo to #div1'></div>\");\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#div1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "extra chars cause error when evaling",
    "html": "",
    "body": "\n\t\tvar msg = await error(\"1!\");\n\t\texpect(msg).toMatch(/^Unexpected Token : !/);\n\n\t\tmsg = await error(\"return 1!\");\n\t\texpect(msg).toMatch(/^Unexpected Token : !/);\n\n\t\tmsg = await error(\"init set x to 1!\");\n\t\texpect(msg).toMatch(/^Unexpected Token : !/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "listen for event on form",
    "html": "<form>  <button id='b1' _='on click from closest <form/> put \"clicked\" into me'>Button</button></form>",
    "body": "\n\t\tawait html(\"<form>\" +\n\t\t\t\"  <button id='b1' _='on click from closest <form/> put \\\"clicked\\\" into me'>Button</button>\" +\n\t\t\t\"</form>\");\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "me and it is properly set when responding to events",
    "html": "<div id='name'></div><div _='on click from #name set window.me to me set window.it to it'></div>",
    "body": "\n\t\tawait html(\"<div id='name'></div>\" +\n\t\t\t\"<div _='on click from #name set window.me to me set window.it to it'></div>\");\n\t\tawait find('#name').dispatchEvent('click');\n\t\tconst meMatch = await evaluate(() => window.me === document.querySelector('#work-area div:nth-of-type(2)'));\n\t\texpect(meMatch).toBe(true);\n\t\tconst itMatch = await evaluate(() => window.it === document.querySelector('#name'));\n\t\texpect(itMatch).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "me symbol works in from expressions",
    "html": "<div><div id='d1' _='on click from closest parent <div/> put \"Foo\" into me'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div>\" + \"<div id='d1' _='on click from closest parent <div/> put \\\"Foo\\\" into me'></div>\" + \"</div>\"\n\t\t);\n\t\tawait expect(find('#d1')).toHaveText(\"\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"Foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "properly interpolates values",
    "html": "<button _='on click                        set count to 1                         set optName to `options_${count}_value`                        put optName into me'></button>",
    "body": "\n\t\tawait html(\"<button _='on click\" +\n\t\t\t\"                        set count to 1 \" +\n\t\t\t\"                        set optName to `options_${count}_value`\" +\n\t\t\t\"                        put optName into me'></button>\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('button')).toHaveText(\"options_1_value\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "properly interpolates values 2",
    "html": "<button _='on click                        set trackingcode to `AB123456789KK`                         set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf`                        put pdfurl into me'></button>",
    "body": "\n\t\tawait html(\"<button _='on click\" +\n\t\t\t\"                        set trackingcode to `AB123456789KK` \" +\n\t\t\t\"                        set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf`\" +\n\t\t\t\"                        put pdfurl into me'></button>\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('button')).toHaveText(\"https://yyy.xxxxxx.com/path/out/AB123456789KK.pdf\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/regressions",
    "name": "string literals can dot-invoked against",
    "html": "",
    "body": "\n\t\texpect(await run(\"'foo'.length\")).toBe(3);\n\t\texpect(await run(\"`foo`.length\")).toBe(3);\n\t\texpect(await run(\"\\\"foo\\\".length\")).toBe(3);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "core/runtime",
    "name": "arrays args are handled properly wrt Promises",
    "html": "<script type='text/hyperscript'>def invokesArrayPromise()   return {            foo: stringPromise(),            bar: stringPromise(),            baz: stringPromise()         }end  def stringPromise()   wait 20ms   return 'foo' end </script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def invokesArrayPromise() \" +\n\t\t\t\t\"  return {\" +\n\t\t\t\t\"            foo: stringPromise(),\" +\n\t\t\t\t\"            bar: stringPromise(),\" +\n\t\t\t\t\"            baz: stringPromise()\" +\n\t\t\t    \"         }\" +\n\t\t\t\t\"end \" +\n\t\t\t\t\" \" +\n\t\t\t\t\"def stringPromise() \" +\n\t\t\t\t\"  wait 20ms \" +\n\t\t\t\t\"  return 'foo' \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\tconst result = await evaluate(() => invokesArrayPromise());\n\t\texpect(result.foo).toBe('foo');\n\t\texpect(result.bar).toBe('foo');\n\t\texpect(result.baz).toBe('foo');\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "async hypertrace is reasonable",
    "html": "<script type='text/hyperscript'>def bar()   call baz('nope') end  def baz(str)   wait 20ms   throw str end </script><div _='on click call bar()'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def bar() \" +\n\t\t\t\t\"  call baz('nope') \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\" \" +\n\t\t\t\t\"def baz(str) \" +\n\t\t\t\t\"  wait 20ms \" +\n\t\t\t\t\"  throw str \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div _='on click call bar()'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\t// Just wait a bit for the async to complete without crashing\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 100)));\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "has proper stack",
    "html": "<script type='text/hyperscript'>def foo()   return bar() end def bar()   return meta.caller end </script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def foo() \" +\n\t\t\t\t\"  return bar() \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"def bar() \" +\n\t\t\t\t\"  return meta.caller \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\tconst name = await evaluate(() => foo().meta.feature.name);\n\t\texpect(name).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "has proper stack from event handler",
    "html": "<script type='text/hyperscript'>def bar()   log meta.caller   return meta.caller end </script><div _='on click put bar().meta.feature.type into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def bar() \" +\n\t\t\t\t\"  log meta.caller \" +\n\t\t\t\t\"  return meta.caller \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div _='on click put bar().meta.feature.type into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"onFeature\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "hypertrace from javascript is reasonable",
    "html": "<script type='text/hyperscript'>def bar() call baz('nope') end</script><div _='on click call bar()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.baz = function (str) { throw new Error(str); };\n\t\t});\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def bar() call baz('nope') end</script>\" +\n\t\t\t\t\"<div _='on click call bar()'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "hypertrace is reasonable",
    "html": "<script type='text/hyperscript'>def bar()   call baz('nope') end  def baz(str)   throw str end </script><div _='on click call bar()'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def bar() \" +\n\t\t\t\t\"  call baz('nope') \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\" \" +\n\t\t\t\t\"def baz(str) \" +\n\t\t\t\t\"  throw str \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div _='on click call bar()'></div>\"\n\t\t);\n\t\t// Should not crash\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtime",
    "name": "scalar args are handled properly wrt Promises",
    "html": "<script type='text/hyperscript'>def invokesScalarPromise()   return stringPromise()end  def stringPromise()   wait 20ms   return 'foo' end </script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def invokesScalarPromise() \" +\n\t\t\t\t\"  return stringPromise()\" +\n\t\t\t\t\"end \" +\n\t\t\t\t\" \" +\n\t\t\t\t\"def stringPromise() \" +\n\t\t\t\t\"  wait 20ms \" +\n\t\t\t\t\"  return 'foo' \" +\n\t\t\t\t\"end \" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\tconst result = await evaluate(() => invokesScalarPromise());\n\t\texpect(result).toBe('foo');\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports basic function invocation null errors properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"x()\")).toBe(\"'x' is null\");\n\t\texpect(await error(\"x.y()\")).toBe(\"'x' is null\");\n\t\texpect(await error(\"x.y.z()\")).toBe(\"'x.y' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports basic function invocation null errors properly w/ of",
    "html": "",
    "body": "\n\t\texpect(await error(\"z() of y of x\")).toBe(\"'z' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports basic function invocation null errors properly w/ possessives",
    "html": "",
    "body": "\n\t\texpect(await error(\"x's y()\")).toBe(\"'x' is null\");\n\t\texpect(await error(\"x's y's z()\")).toBe(\"'x's y' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on add command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"add .foo to #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"add @foo to #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"add {display:none} to #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on decrement command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"decrement #doesntExist's innerHTML\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on default command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"default #doesntExist's innerHTML to 'foo'\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on hide command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"hide #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on increment command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"increment #doesntExist's innerHTML\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on measure command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"measure #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on put command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"put 'foo' into #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' into #doesntExist's innerHTML\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' into #doesntExist.innerHTML\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' before #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' after #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' at the start of #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"put 'foo' at the end of #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on remove command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"remove .foo from #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"remove @foo from #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"remove #doesntExist from #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on send command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"send 'foo' to #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on sets properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"set x's y to true\")).toBe(\"'x' is null\");\n\t\texpect(await error(\"set x's @y to true\")).toBe(\"'x' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on settle command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"settle #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on show command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"show #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on toggle command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"toggle .foo on #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"toggle between .foo and .bar on #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t\texpect(await error(\"toggle @foo on #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on transition command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"transition #doesntExist's *visibility to 0\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/runtimeErrors",
    "name": "reports null errors on trigger command properly",
    "html": "",
    "body": "\n\t\texpect(await error(\"trigger 'foo' on #doesntExist\")).toBe(\"'#doesntExist' is null\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "basic behavior scoping works",
    "html": "<script type=text/hyperscript>behavior Behave(foo) on click set @out to foo</script><div id='d1' _='install Behave(foo:10)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(foo) on click set @out to foo\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d1' _='install Behave(foo:10)'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "behavior scoping is at the element level",
    "html": "<script type=text/hyperscript>behavior Behave(foo)   on click 1 set foo to foo + 10  on click 2 set @out to foo</script><div id='d1' _='install Behave(foo:10)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(foo) \" +\n\t\t\t\"  on click 1 set foo to foo + 10\" +\n\t\t\t\"  on click 2 set @out to foo\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d1' _='install Behave(foo:10)'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '20');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "behavior scoping is isolated from other behaviors",
    "html": "<script type=text/hyperscript>behavior Behave(foo)   on click 1 set foo to foo + 10  on click 3 set @out to foo behavior BehaveTwo(foo)   on click 2 set element foo to 1   on click 4 set @out2 to foo</script><div id='d1' _='install Behave(foo:10) install BehaveTwo(foo:42)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(foo) \" +\n\t\t\t\"  on click 1 set foo to foo + 10\" +\n\t\t\t\"  on click 3 set @out to foo\" +\n\t\t\t\" behavior BehaveTwo(foo) \" +\n\t\t\t\"  on click 2 set element foo to 1 \" +\n\t\t\t\"  on click 4 set @out2 to foo\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d1' _='install Behave(foo:10) install BehaveTwo(foo:42)'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '20');\n\t\tawait expect(find('#d1')).toHaveAttribute('out2', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "behavior scoping is isolated from the core element scope",
    "html": "<script type=text/hyperscript>behavior Behave(foo)   on click 1 set foo to foo + 10  on click 3 set @out to foo</script><div id='d1' _='install Behave(foo:10) on click 2 set element foo to 1 on click 4 set @out2 to foo'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior Behave(foo) \" +\n\t\t\t\"  on click 1 set foo to foo + 10\" +\n\t\t\t\"  on click 3 set @out to foo\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d1' _='install Behave(foo:10) on click 2 set element foo to 1 on click 4 set @out2 to foo'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '20');\n\t\tawait expect(find('#d1')).toHaveAttribute('out2', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables are local only to element",
    "html": "<div id='d1' _='on click set element x to 10'>         <div id='d2' _='on click set @out to x'>       </div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set element x to 10'>\" +\n\t\t\t\"         <div id='d2' _='on click set @out to x'>\" +\n\t\t\t\"       </div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tconst out1 = await evaluate(() => document.querySelector('#d1').getAttribute(\"out\"));\n\t\tconst out2 = await evaluate(() => document.querySelector('#d2').getAttribute(\"out\"));\n\t\texpect(out1).toBeNull();\n\t\texpect(out2).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables span features",
    "html": "<div id='d1' _='on click 1 set element x to 10                        on click 2 set @out to x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 set element x to 10 \" +\n\t\t\t\"                       on click 2 set @out to x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables span features w/short syntax",
    "html": "<div id='d1' _='on click 1 set :x to 10                        on click 2 set @out to :x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 set :x to 10 \" +\n\t\t\t\"                       on click 2 set @out to :x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables support pseudo-possessive syntax",
    "html": "<div id=\"d1\" _=\"on click set the element's x to 10 then set @out to the element's x\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div id=\"d1\" _=\"on click set the element\\'s x to 10 then set @out to the element\\'s x\"></div>'\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables work",
    "html": "<div id='d1' _='on click set element x to 10 then set @out to x'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set element x to 10 then set @out to x'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "element scoped variables work w/short syntax",
    "html": "<div id='d1' _='on click set :x to 10 then set @out to :x'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set :x to 10 then set @out to :x'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "global scoped variables span features",
    "html": "<div id='d1' _='on click 1 set $x to 10                        on click 2 set @out to $x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 set $x to 10 \" +\n\t\t\t\"                       on click 2 set @out to $x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "global scoped variables work",
    "html": "<div id='d1' _='on click set global x to 10 then set @out to x'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set global x to 10 then set @out to x'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "global scoped variables work w/ short syntax",
    "html": "<div id='d1' _='on click set $x to 10 then set @out to $x'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set $x to 10 then set @out to $x'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "locally scoped variables do not span features",
    "html": "<div id='d1' _='on click 1 set x to 10                        on click 2 set @out to x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 set x to 10 \" +\n\t\t\t\"                       on click 2 set @out to x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst attr = await evaluate(() => document.querySelector('#d1').getAttribute(\"out\"));\n\t\texpect(attr).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "locally scoped variables don't clash with built-in variables",
    "html": "<div id='d1' _='on click repeat for meta in [1, 2, 3] set @out to meta end'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click repeat for meta in [1, 2, 3] set @out to meta end'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '[object Object]');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "locally scoped variables work",
    "html": "<div id='d1' _='on click set x to 10 then set @out to x'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set x to 10 then set @out to x'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "set favors local variables over global variables",
    "html": "<div id='d1' _='on click 1 set foo to 20 then set @out to foo'></div>",
    "body": "\n\t\tawait evaluate(() => window.foo = 12);\n\t\tawait html(\"<div id='d1' _='on click 1 set foo to 20 then set @out to foo'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '20');\n\t\texpect(await evaluate(() => window.foo)).toBe(12);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "setting a global scoped variable spans features",
    "html": "<div id='d1' _='on click 1 default global x to 0                       on click 2 set global x to 10                        on click 3 set @out to x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 default global x to 0\" +\n\t\t\t\"                       on click 2 set global x to 10 \" +\n\t\t\t\"                       on click 3 set @out to x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "setting an element scoped variable spans features",
    "html": "<div id='d1' _='on click 1 default element x to 0                       on click 2 set element x to 10                        on click 3 set @out to x'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click 1 default element x to 0\" +\n\t\t\t\"                       on click 2 set element x to 10 \" +\n\t\t\t\"                       on click 3 set @out to x'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/scoping",
    "name": "variables are hoisted",
    "html": "<div id='d1' _='on click if true set foo to 10 end set @out to foo'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click if true set foo to 10 end set @out to foo'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveAttribute('out', '10');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/security",
    "name": "on a single div",
    "html": "<div disable-scripting><div id='d1' _='on click add .foo'></div></div>",
    "body": "\n\t\tawait html(\"<div disable-scripting>\" + \"<div id='d1' _='on click add .foo'></div>\" + \"</div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "core/sourceInfo",
    "name": "debug",
    "html": "",
    "body": "\n\t\tconst src = await evaluate(() => _hyperscript.parse(\"<button.foo/>\").sourceFor());\n\t\texpect(src).toBe(\"<button.foo/>\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/sourceInfo",
    "name": "get line works for statements",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tlet elt = _hyperscript.parse(\"if true\\n  log 'it was true'\\n    log 'it was true'\");\n\t\t\treturn {\n\t\t\t\tline: elt.lineFor(),\n\t\t\t\ttrueBranchLine: elt.trueBranch.lineFor(),\n\t\t\t\tnextLine: elt.trueBranch.next.lineFor()\n\t\t\t};\n\t\t});\n\t\texpect(result.line).toBe(\"if true\");\n\t\texpect(result.trueBranchLine).toBe(\"  log 'it was true'\");\n\t\texpect(result.nextLine).toBe(\"    log 'it was true'\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/sourceInfo",
    "name": "get source works for expressions",
    "html": "",
    "body": "\n\t\tlet result = await evaluate(() => {\n\t\t\tlet elt = _hyperscript.parse(\"1\");\n\t\t\treturn elt.sourceFor();\n\t\t});\n\t\texpect(result).toBe(\"1\");\n\n\t\tresult = await evaluate(() => {\n\t\t\tlet elt = _hyperscript.parse(\"a.b\");\n\t\t\treturn { src: elt.sourceFor(), rootSrc: elt.root.sourceFor() };\n\t\t});\n\t\texpect(result.src).toBe(\"a.b\");\n\t\texpect(result.rootSrc).toBe(\"a\");\n\n\t\tresult = await evaluate(() => {\n\t\t\tlet elt = _hyperscript.parse(\"a.b()\");\n\t\t\treturn {\n\t\t\t\tsrc: elt.sourceFor(),\n\t\t\t\trootSrc: elt.root.sourceFor(),\n\t\t\t\trootRootSrc: elt.root.root.sourceFor()\n\t\t\t};\n\t\t});\n\t\texpect(result.src).toBe(\"a.b()\");\n\t\texpect(result.rootSrc).toBe(\"a.b\");\n\t\texpect(result.rootRootSrc).toBe(\"a\");\n\n\t\tresult = await evaluate(() => _hyperscript.parse(\"<button.foo/>\").sourceFor());\n\t\texpect(result).toBe(\"<button.foo/>\");\n\n\t\tresult = await evaluate(() => {\n\t\t\tlet elt = _hyperscript.parse(\"x + y\");\n\t\t\treturn { src: elt.sourceFor(), lhs: elt.lhs.sourceFor(), rhs: elt.rhs.sourceFor() };\n\t\t});\n\t\texpect(result.src).toBe(\"x + y\");\n\t\texpect(result.lhs).toBe(\"x\");\n\t\texpect(result.rhs).toBe(\"y\");\n\n\t\tresult = await evaluate(() => _hyperscript.parse(\"'foo'\").sourceFor());\n\t\texpect(result).toBe(\"'foo'\");\n\n\t\tresult = await evaluate(() => _hyperscript.parse(\".foo\").sourceFor());\n\t\texpect(result).toBe(\".foo\");\n\n\t\tresult = await evaluate(() => _hyperscript.parse(\"#bar\").sourceFor());\n\t\texpect(result).toBe(\"#bar\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/sourceInfo",
    "name": "get source works for statements",
    "html": "",
    "body": "\n\t\tlet result = await evaluate(() => _hyperscript.parse(\"if true log 'it was true'\").sourceFor());\n\t\texpect(result).toBe(\"if true log 'it was true'\");\n\n\t\tresult = await evaluate(() => _hyperscript.parse(\"for x in [1, 2, 3] log x then log x end\").sourceFor());\n\t\texpect(result).toBe(\"for x in [1, 2, 3] log x then log x end\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles $ in template properly",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn t.tokenize('\"', true).token(0).value;\n\t\t});\n\t\texpect(result).toBe('\"');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles all special escapes properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\tt.tokenize('\"\\\\b\"').consumeToken().value,\n\t\t\t\tt.tokenize('\"\\\\f\"').consumeToken().value,\n\t\t\t\tt.tokenize('\"\\\\n\"').consumeToken().value,\n\t\t\t\tt.tokenize('\"\\\\r\"').consumeToken().value,\n\t\t\t\tt.tokenize('\"\\\\t\"').consumeToken().value,\n\t\t\t\tt.tokenize('\"\\\\v\"').consumeToken().value,\n\t\t\t];\n\t\t});\n\t\texpect(results[0]).toBe(\"\\b\");\n\t\texpect(results[1]).toBe(\"\\f\");\n\t\texpect(results[2]).toBe(\"\\n\");\n\t\texpect(results[3]).toBe(\"\\r\");\n\t\texpect(results[4]).toBe(\"\\t\");\n\t\texpect(results[5]).toBe(\"\\v\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles basic token types",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst tokenizer = _hyperscript.internals.tokenizer;\n\t\t\tconst r = {};\n\n\t\t\tr.fooType = tokenizer.tokenize(\"foo\").consumeToken().type;\n\t\t\tr.numType = tokenizer.tokenize(\"1\").consumeToken().type;\n\n\t\t\tlet tokens = tokenizer.tokenize(\"1.1\");\n\t\t\tr.floatType = tokens.consumeToken().type;\n\t\t\tr.floatHasMore = tokens.hasMore();\n\n\t\t\ttokens = tokenizer.tokenize(\"1e6\");\n\t\t\tr.sciType = tokens.consumeToken().type;\n\t\t\tr.sciHasMore = tokens.hasMore();\n\n\t\t\ttokens = tokenizer.tokenize(\"1e-6\");\n\t\t\tr.sciNegType = tokens.consumeToken().type;\n\t\t\tr.sciNegHasMore = tokens.hasMore();\n\n\t\t\ttokens = tokenizer.tokenize(\"1.1e6\");\n\t\t\tr.floatSciType = tokens.consumeToken().type;\n\t\t\tr.floatSciHasMore = tokens.hasMore();\n\n\t\t\ttokens = tokenizer.tokenize(\"1.1e-6\");\n\t\t\tr.floatSciNegType = tokens.consumeToken().type;\n\t\t\tr.floatSciNegHasMore = tokens.hasMore();\n\n\t\t\tr.classType = tokenizer.tokenize(\".a\").consumeToken().type;\n\t\t\tr.idType = tokenizer.tokenize(\"#a\").consumeToken().type;\n\t\t\tr.stringType = tokenizer.tokenize('\"asdf\"').consumeToken().type;\n\n\t\t\treturn r;\n\t\t});\n\n\t\texpect(results.fooType).toBe(\"IDENTIFIER\");\n\t\texpect(results.numType).toBe(\"NUMBER\");\n\t\texpect(results.floatType).toBe(\"NUMBER\");\n\t\texpect(results.floatHasMore).toBe(false);\n\t\texpect(results.sciType).toBe(\"NUMBER\");\n\t\texpect(results.sciHasMore).toBe(false);\n\t\texpect(results.sciNegType).toBe(\"NUMBER\");\n\t\texpect(results.sciNegHasMore).toBe(false);\n\t\texpect(results.floatSciType).toBe(\"NUMBER\");\n\t\texpect(results.floatSciHasMore).toBe(false);\n\t\texpect(results.floatSciNegType).toBe(\"NUMBER\");\n\t\texpect(results.floatSciNegHasMore).toBe(false);\n\t\texpect(results.classType).toBe(\"CLASS_REF\");\n\t\texpect(results.idType).toBe(\"ID_REF\");\n\t\texpect(results.stringType).toBe(\"STRING\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles class identifiers properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\t{type: t.tokenize(\".a\").consumeToken().type, value: t.tokenize(\".a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"  .a\").consumeToken().type, value: t.tokenize(\"  .a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"a.a\").consumeToken().type, value: t.tokenize(\"a.a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"(a).a\").list[4].type, value: t.tokenize(\"(a).a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"{a}.a\").list[4].type, value: t.tokenize(\"{a}.a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"[a].a\").list[4].type, value: t.tokenize(\"[a].a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"(a(.a\").list[3].type, value: t.tokenize(\"(a(.a\").list[3].value},\n\t\t\t\t{type: t.tokenize(\"{a{.a\").list[3].type, value: t.tokenize(\"{a{.a\").list[3].value},\n\t\t\t\t{type: t.tokenize(\"[a[.a\").list[3].type, value: t.tokenize(\"[a[.a\").list[3].value},\n\t\t\t];\n\t\t});\n\t\texpect(results[0]).toEqual({type: \"CLASS_REF\", value: \".a\"});\n\t\texpect(results[1]).toEqual({type: \"CLASS_REF\", value: \".a\"});\n\t\texpect(results[2]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[3]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[4]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[5]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[6]).toEqual({type: \"CLASS_REF\", value: \".a\"});\n\t\texpect(results[7]).toEqual({type: \"CLASS_REF\", value: \".a\"});\n\t\texpect(results[8]).toEqual({type: \"CLASS_REF\", value: \".a\"});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles comments properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\tt.tokenize(\"--\").list.length,\n\t\t\t\tt.tokenize(\"asdf--\").list.length,\n\t\t\t\tt.tokenize(\"-- asdf\").list.length,\n\t\t\t\tt.tokenize(\"--\\nasdf\").list.length,\n\t\t\t\tt.tokenize(\"--\\nasdf--\").list.length,\n\t\t\t\tt.tokenize(\"---asdf\").list.length,\n\t\t\t\tt.tokenize(\"----\\n---asdf\").list.length,\n\t\t\t\tt.tokenize(\"----asdf----\").list.length,\n\t\t\t\tt.tokenize(\"---\\nasdf---\").list.length,\n\t\t\t\tt.tokenize(\"// asdf\").list.length,\n\t\t\t\tt.tokenize(\"///asdf\").list.length,\n\t\t\t\tt.tokenize(\"asdf//\").list.length,\n\t\t\t\tt.tokenize(\"asdf\\n//\").list.length,\n\t\t\t];\n\t\t});\n\t\texpect(results).toEqual([0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 2]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles hex escapes properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst r = [];\n\t\t\tr.push(t.tokenize('\"\\\\x1f\"').consumeToken().value);\n\t\t\tr.push(t.tokenize('\"\\\\x41\"').consumeToken().value);\n\t\t\tr.push(t.tokenize('\"\\\\x41\\\\x61\"').consumeToken().value);\n\n\t\t\tconst errors = [];\n\t\t\ttry { t.tokenize('\"\\\\x\"').consumeToken(); } catch (e) { errors.push(e.message); }\n\t\t\ttry { t.tokenize('\"\\\\xGG\"').consumeToken(); } catch (e) { errors.push(e.message); }\n\t\t\ttry { t.tokenize('\"\\\\1H\"').consumeToken(); } catch (e) { errors.push(e.message); }\n\t\t\ttry { t.tokenize('\"\\\\x4\"').consumeToken(); } catch (e) { errors.push(e.message); }\n\n\t\t\treturn {values: r, errors: errors};\n\t\t});\n\t\texpect(results.values[0]).toBe(\"\\x1f\");\n\t\texpect(results.values[1]).toBe(\"A\");\n\t\texpect(results.values[2]).toBe(\"Aa\");\n\t\tfor (const msg of results.errors) {\n\t\t\texpect(msg).toMatch(/Invalid hexadecimal escape/);\n\t\t}\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles id references properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\t{type: t.tokenize(\"#a\").consumeToken().type, value: t.tokenize(\"#a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"  #a\").consumeToken().type, value: t.tokenize(\"  #a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"a#a\").consumeToken().type, value: t.tokenize(\"a#a\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"(a)#a\").list[4].type, value: t.tokenize(\"(a)#a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"{a}#a\").list[4].type, value: t.tokenize(\"{a}#a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"[a]#a\").list[4].type, value: t.tokenize(\"[a]#a\").list[4].value},\n\t\t\t\t{type: t.tokenize(\"(a(#a\").list[3].type, value: t.tokenize(\"(a(#a\").list[3].value},\n\t\t\t\t{type: t.tokenize(\"{a{#a\").list[3].type, value: t.tokenize(\"{a{#a\").list[3].value},\n\t\t\t\t{type: t.tokenize(\"[a[#a\").list[3].type, value: t.tokenize(\"[a[#a\").list[3].value},\n\t\t\t];\n\t\t});\n\t\texpect(results[0]).toEqual({type: \"ID_REF\", value: \"#a\"});\n\t\texpect(results[1]).toEqual({type: \"ID_REF\", value: \"#a\"});\n\t\texpect(results[2]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[3]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[4]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[5]).toEqual({type: \"IDENTIFIER\", value: \"a\"});\n\t\texpect(results[6]).toEqual({type: \"ID_REF\", value: \"#a\"});\n\t\texpect(results[7]).toEqual({type: \"ID_REF\", value: \"#a\"});\n\t\texpect(results[8]).toEqual({type: \"ID_REF\", value: \"#a\"});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles identifiers properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst r = {};\n\n\t\t\tlet token = t.tokenize(\"foo\").consumeToken();\n\t\t\tr.foo = {type: token.type, value: token.value};\n\n\t\t\ttoken = t.tokenize(\"     foo    \").consumeToken();\n\t\t\tr.fooSpaces = {type: token.type, value: token.value};\n\n\t\t\tlet tokens = t.tokenize(\"     foo    bar\");\n\t\t\ttoken = tokens.consumeToken();\n\t\t\tr.fooFirst = {type: token.type, value: token.value};\n\t\t\ttoken = tokens.consumeToken();\n\t\t\tr.barSecond = {type: token.type, value: token.value};\n\n\t\t\ttokens = t.tokenize(\"     foo\\n-- a comment\\n    bar\");\n\t\t\ttoken = tokens.consumeToken();\n\t\t\tr.fooComment = {type: token.type, value: token.value};\n\t\t\ttoken = tokens.consumeToken();\n\t\t\tr.barComment = {type: token.type, value: token.value};\n\n\t\t\treturn r;\n\t\t});\n\n\t\texpect(results.foo).toEqual({type: \"IDENTIFIER\", value: \"foo\"});\n\t\texpect(results.fooSpaces).toEqual({type: \"IDENTIFIER\", value: \"foo\"});\n\t\texpect(results.fooFirst).toEqual({type: \"IDENTIFIER\", value: \"foo\"});\n\t\texpect(results.barSecond).toEqual({type: \"IDENTIFIER\", value: \"bar\"});\n\t\texpect(results.fooComment).toEqual({type: \"IDENTIFIER\", value: \"foo\"});\n\t\texpect(results.barComment).toEqual({type: \"IDENTIFIER\", value: \"bar\"});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles identifiers with numbers properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\t{type: t.tokenize(\"f1oo\").consumeToken().type, value: t.tokenize(\"f1oo\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"fo1o\").consumeToken().type, value: t.tokenize(\"fo1o\").consumeToken().value},\n\t\t\t\t{type: t.tokenize(\"foo1\").consumeToken().type, value: t.tokenize(\"foo1\").consumeToken().value},\n\t\t\t];\n\t\t});\n\t\texpect(results[0]).toEqual({type: \"IDENTIFIER\", value: \"f1oo\"});\n\t\texpect(results[1]).toEqual({type: \"IDENTIFIER\", value: \"fo1o\"});\n\t\texpect(results[2]).toEqual({type: \"IDENTIFIER\", value: \"foo1\"});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles look ahead property",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst tokenize = t.tokenize(\"a 1 + 1\");\n\t\t\treturn [\n\t\t\t\ttokenize.token(0).value,\n\t\t\t\ttokenize.token(1).value,\n\t\t\t\ttokenize.token(2).value,\n\t\t\t\ttokenize.token(3).value,\n\t\t\t\ttokenize.token(4).value,\n\t\t\t];\n\t\t});\n\t\texpect(results).toEqual([\"a\", \"1\", \"+\", \"1\", \"<<<EOF>>>\"]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles numbers properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst r = [];\n\t\t\tr.push({type: t.tokenize(\"1\").consumeToken().type, value: t.tokenize(\"1\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1.1\").consumeToken().type, value: t.tokenize(\"1.1\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1234567890.1234567890\").consumeToken().type, value: t.tokenize(\"1234567890.1234567890\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1e6\").consumeToken().type, value: t.tokenize(\"1e6\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1e-6\").consumeToken().type, value: t.tokenize(\"1e-6\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1.1e6\").consumeToken().type, value: t.tokenize(\"1.1e6\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"1.1e-6\").consumeToken().type, value: t.tokenize(\"1.1e-6\").consumeToken().value});\n\n\t\t\tconst tokens = t.tokenize(\"1.1.1\").list;\n\t\t\tr.push({t0: tokens[0].type, t1: tokens[1].type, t2: tokens[2].type, len: tokens.length});\n\n\t\t\treturn r;\n\t\t});\n\t\texpect(results[0]).toEqual({type: \"NUMBER\", value: \"1\"});\n\t\texpect(results[1]).toEqual({type: \"NUMBER\", value: \"1.1\"});\n\t\texpect(results[2]).toEqual({type: \"NUMBER\", value: \"1234567890.1234567890\"});\n\t\texpect(results[3]).toEqual({type: \"NUMBER\", value: \"1e6\"});\n\t\texpect(results[4]).toEqual({type: \"NUMBER\", value: \"1e-6\"});\n\t\texpect(results[5]).toEqual({type: \"NUMBER\", value: \"1.1e6\"});\n\t\texpect(results[6]).toEqual({type: \"NUMBER\", value: \"1.1e-6\"});\n\t\texpect(results[7]).toEqual({t0: \"NUMBER\", t1: \"PERIOD\", t2: \"NUMBER\", len: 3});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles operators properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst optable = {\n\t\t\t\t\"+\": \"PLUS\", \"-\": \"MINUS\", \"*\": \"MULTIPLY\", \".\": \"PERIOD\",\n\t\t\t\t\"\\\\\": \"BACKSLASH\", \":\": \"COLON\", \"%\": \"PERCENT\", \"|\": \"PIPE\",\n\t\t\t\t\"!\": \"EXCLAMATION\", \"?\": \"QUESTION\", \"#\": \"POUND\", \"&\": \"AMPERSAND\",\n\t\t\t\t\";\": \"SEMI\", \",\": \"COMMA\", \"(\": \"L_PAREN\", \")\": \"R_PAREN\",\n\t\t\t\t\"<\": \"L_ANG\", \">\": \"R_ANG\", \"{\": \"L_BRACE\", \"}\": \"R_BRACE\",\n\t\t\t\t\"[\": \"L_BRACKET\", \"]\": \"R_BRACKET\", \"=\": \"EQUALS\",\n\t\t\t\t\"<=\": \"LTE_ANG\", \">=\": \"GTE_ANG\", \"==\": \"EQ\", \"===\": \"EQQ\",\n\t\t\t};\n\t\t\tconst r = [];\n\t\t\tObject.keys(optable).forEach(key => {\n\t\t\t\tconst token = t.tokenize(key).consumeToken();\n\t\t\t\tr.push({key: key, op: token.op, value: token.value});\n\t\t\t});\n\t\t\treturn r;\n\t\t});\n\t\tfor (const item of results) {\n\t\t\texpect(item.op).toBe(true);\n\t\t\texpect(item.value).toBe(item.key);\n\t\t}\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles strings properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst r = [];\n\t\t\tr.push({type: t.tokenize('\"foo\"').consumeToken().type, value: t.tokenize('\"foo\"').consumeToken().value});\n\t\t\tr.push({type: t.tokenize('\"fo\\'o\"').consumeToken().type, value: t.tokenize('\"fo\\'o\"').consumeToken().value});\n\t\t\tr.push({type: t.tokenize('\"fo\\\\\"o\"').consumeToken().type, value: t.tokenize('\"fo\\\\\"o\"').consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"'foo'\").consumeToken().type, value: t.tokenize(\"'foo'\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"'fo\\\"o'\").consumeToken().type, value: t.tokenize(\"'fo\\\"o'\").consumeToken().value});\n\t\t\tr.push({type: t.tokenize(\"'fo\\\\'o'\").consumeToken().type, value: t.tokenize(\"'fo\\\\'o'\").consumeToken().value});\n\n\t\t\tlet unterminatedSingle = null;\n\t\t\ttry { t.tokenize(\"'\").consumeToken(); } catch (e) { unterminatedSingle = e.message; }\n\t\t\tr.push(unterminatedSingle);\n\n\t\t\tlet unterminatedDouble = null;\n\t\t\ttry { t.tokenize('\"').consumeToken(); } catch (e) { unterminatedDouble = e.message; }\n\t\t\tr.push(unterminatedDouble);\n\n\t\t\treturn r;\n\t\t});\n\t\texpect(results[0]).toEqual({type: \"STRING\", value: \"foo\"});\n\t\texpect(results[1]).toEqual({type: \"STRING\", value: \"fo'o\"});\n\t\texpect(results[2]).toEqual({type: \"STRING\", value: 'fo\"o'});\n\t\texpect(results[3]).toEqual({type: \"STRING\", value: \"foo\"});\n\t\texpect(results[4]).toEqual({type: \"STRING\", value: 'fo\"o'});\n\t\texpect(results[5]).toEqual({type: \"STRING\", value: \"fo'o\"});\n\t\texpect(results[6]).toMatch(/Unterminated string/);\n\t\texpect(results[7]).toMatch(/Unterminated string/);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles strings properly 2",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst token = t.tokenize(\"'foo'\").consumeToken();\n\t\t\treturn {type: token.type, value: token.value};\n\t\t});\n\t\texpect(result).toEqual({type: \"STRING\", value: \"foo\"});\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles template bootstrap properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\tconst r = [];\n\n\t\t\tr.push(t.tokenize('\"', true).token(0).value);\n\n\t\t\tlet tok = t.tokenize('\"$', true);\n\t\t\tr.push([tok.token(0).value, tok.token(1).value]);\n\n\t\t\ttok = t.tokenize('\"${', true);\n\t\t\tr.push([tok.token(0).value, tok.token(1).value, tok.token(2).value]);\n\n\t\t\ttok = t.tokenize('\"${\"asdf\"', true);\n\t\t\tr.push([tok.token(0).value, tok.token(1).value, tok.token(2).value, tok.token(3).value]);\n\n\t\t\ttok = t.tokenize('\"${\"asdf\"}\"', true);\n\t\t\tr.push([tok.token(0).value, tok.token(1).value, tok.token(2).value, tok.token(3).value, tok.token(4).value, tok.token(5).value]);\n\n\t\t\treturn r;\n\t\t});\n\t\texpect(results[0]).toBe('\"');\n\t\texpect(results[1]).toEqual(['\"', '$']);\n\t\texpect(results[2]).toEqual(['\"', '$', '{']);\n\t\texpect(results[3]).toEqual(['\"', '$', '{', 'asdf']);\n\t\texpect(results[4]).toEqual(['\"', '$', '{', 'asdf', '}', '\"']);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "handles whitespace properly",
    "html": "",
    "body": "\n\t\tconst results = await evaluate(() => {\n\t\t\tconst t = _hyperscript.internals.tokenizer;\n\t\t\treturn [\n\t\t\t\tt.tokenize(\"   \").list.length,\n\t\t\t\tt.tokenize(\"  asdf\").list.length,\n\t\t\t\tt.tokenize(\"  asdf  \").list.length,\n\t\t\t\tt.tokenize(\"asdf  \").list.length,\n\t\t\t\tt.tokenize(\"\\n\").list.length,\n\t\t\t\tt.tokenize(\"\\nasdf\").list.length,\n\t\t\t\tt.tokenize(\"\\nasdf\\n\").list.length,\n\t\t\t\tt.tokenize(\"asdf\\n\").list.length,\n\t\t\t\tt.tokenize(\"\\r\").list.length,\n\t\t\t\tt.tokenize(\"\\rasdf\").list.length,\n\t\t\t\tt.tokenize(\"\\rasdf\\r\").list.length,\n\t\t\t\tt.tokenize(\"asdf\\r\").list.length,\n\t\t\t\tt.tokenize(\"\\t\").list.length,\n\t\t\t\tt.tokenize(\"\\tasdf\").list.length,\n\t\t\t\tt.tokenize(\"\\tasdf\\t\").list.length,\n\t\t\t\tt.tokenize(\"asdf\\t\").list.length,\n\t\t\t];\n\t\t});\n\t\texpect(results).toEqual([0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "core/tokenizer",
    "name": "string interpolation isnt surprising",
    "html": "<div _=\"on click set x to 42 then put `test\\${x} test ${x} test\\$x test $x test \\$x test \\${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML\"></div>",
    "body": "\n\t\tawait html('<div _=\"on click set x to 42 then put `test\\\\${x} test ${x} test\\\\$x test $x test \\\\$x test \\\\${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML\"></div>');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"test${x} test 42 test$x test 42 test $x test ${x} test42 test_42 test_42 test-42 test.42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "def",
    "name": "async finally blocks run normally",
    "html": "<script type='text/hyperscript'>def foo()   wait a tick then set window.bar to 10 finally    set window.bar to 20 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   wait a tick then set window.bar to 10 finally    set window.bar to 20 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tawait expect.poll(() => evaluate(() => window.bar)).toBe(20)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "async finally blocks run when an exception occurs",
    "html": "<script type='text/hyperscript'>def foo()   wait a tick then set window.bar to 10  throw \"foo\" finally    set window.bar to 20 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   wait a tick then set window.bar to 10  throw \\\"foo\\\" finally    set window.bar to 20 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tawait expect.poll(() => evaluate(() => window.bar)).toBe(20)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can call asynchronously",
    "html": "<script type='text/hyperscript'>def foo()   wait 1ms  log me end</script><div _='on click call foo() then add .called to #d1'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo()   wait 1ms  log me end</script>\" +\n\t\t\t\"<div _='on click call foo() then add .called to #d1'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can catch async exceptions",
    "html": "<script type='text/hyperscript'>def doh()   wait 10ms  throw \"bar\" end def foo()   call doh() catch e    set window.bar to e end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def doh()   wait 10ms  throw \\\"bar\\\" end \" +\n\t\t\t\"def foo()   call doh() catch e    set window.bar to e end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tawait expect.poll(() => evaluate(() => window.bar)).toBe('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can catch exceptions",
    "html": "<script type='text/hyperscript'>def foo()   throw \"bar\" catch e    set window.bar to e end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   throw \\\"bar\\\" catch e    set window.bar to e end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tconst result = await evaluate(() => window.bar)\n\t\texpect(result).toBe('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can catch nested async exceptions",
    "html": "<script type='text/hyperscript'>def doh()   wait 10ms  throw \"bar\" end def foo()   call doh() catch e    set window.bar to e end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def doh()   wait 10ms  throw \\\"bar\\\" end \" +\n\t\t\t\"def foo()   call doh() catch e    set window.bar to e end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tawait expect.poll(() => evaluate(() => window.bar)).toBe('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can define a basic no arg function",
    "html": "<script type='text/hyperscript'>def foo()   add .called to #d1 end</script><div _='on click call foo()'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo()   add .called to #d1 end</script>\" +\n\t\t\t\"<div _='on click call foo()'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can define a basic one arg function",
    "html": "<script type='text/hyperscript'>def foo(str) put str into #d1.innerHTML end</script><div _='on click call foo(\"called\")'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo(str) put str into #d1.innerHTML end</script>\" +\n\t\t\t\"<div _='on click call foo(\\\"called\\\")'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).toHaveText('')\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText('called')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can exit",
    "html": "<script type='text/hyperscript'>def foo()   exit end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>def foo()   exit end</script>\")\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can install a function on an element and use in children w/ no leak",
    "html": "<div _='def func() put 42 into #d3'><div id='d1' _='on click call func()'></div><div id='d2'></div><div id='d3'></div> </div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='def func() put 42 into #d3'>\" +\n\t\t\t\"<div id='d1' _='on click call func()'></div><div id='d2'></div><div id='d3'></div> </div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d3')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "def",
    "name": "can install a function on an element and use in children w/ return value",
    "html": "<div _='def func() return 42'><div id='d1' _='on click put func() into me'></div><div id='d2'></div><div id='d3'></div> </div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='def func() return 42'>\" +\n\t\t\t\"<div id='d1' _='on click put func() into me'></div><div id='d2'></div><div id='d3'></div> </div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "def",
    "name": "can install a function on an element and use me symbol correctly",
    "html": "<div id='outer' _='def func() put 42 into me'><div id='d1' _='on click call func()'></div><div id='d2'></div><div id='d3'></div> </div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='def func() put 42 into me'>\" +\n\t\t\t\"<div id='d1' _='on click call func()'></div><div id='d2'></div><div id='d3'></div> </div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#outer')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "def",
    "name": "can interop with javascript",
    "html": "<script type='text/hyperscript'>def foo()   return \"foo\" end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>def foo()   return \\\"foo\\\" end</script>\")\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBe('foo')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can interop with javascript asynchronously",
    "html": "<script type='text/hyperscript'>def foo()   wait 1ms  return \"foo\" end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>def foo()   wait 1ms  return \\\"foo\\\" end</script>\")\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBe('foo')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can rethrow in async catch blocks",
    "html": "<script type='text/hyperscript'>def foo()   throw \"bar\" catch e    wait 10ms    throw e end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   throw \\\"bar\\\" catch e    wait 10ms    throw e end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tconst result = await evaluate(() => foo().catch(reason => reason))\n\t\texpect(result).toBe('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can rethrow in catch blocks",
    "html": "<script type='text/hyperscript'>def foo()   throw \"bar\" catch e    throw e end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   throw \\\"bar\\\" catch e    throw e end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tconst result = await evaluate(() => {\n\t\t\ttry {\n\t\t\t\tfoo()\n\t\t\t\treturn 'no-error'\n\t\t\t} catch (e) {\n\t\t\t\treturn e\n\t\t\t}\n\t\t})\n\t\texpect(result).toBe('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can return a value asynchronously",
    "html": "<script type='text/hyperscript'>def foo()   wait 1ms  return \"foo\" end</script><div _='on click call foo() then put it into #d1.innerText'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo()   wait 1ms  return \\\"foo\\\" end</script>\" +\n\t\t\t\"<div _='on click call foo() then put it into #d1.innerText'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).toHaveText('')\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText('foo')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can return a value synchronously",
    "html": "<script type='text/hyperscript'>def foo()   return \"foo\" end</script><div _='on click call foo() then put it into #d1.innerText'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo()   return \\\"foo\\\" end</script>\" +\n\t\t\t\"<div _='on click call foo() then put it into #d1.innerText'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).toHaveText('')\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText('foo')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can return in async catch blocks",
    "html": "<script type='text/hyperscript'>def foo()   throw \"bar\" catch e    wait 10ms    return 42 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   throw \\\"bar\\\" catch e    wait 10ms    return 42 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tconst result = await evaluate(() => foo().then(val => val))\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can return in catch blocks",
    "html": "<script type='text/hyperscript'>def foo()   throw \"bar\" catch e    return 42 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   throw \\\"bar\\\" catch e    return 42 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "can return without a value",
    "html": "<script type='text/hyperscript'>def foo()   return end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>def foo()   return end</script>\")\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "exit stops execution mid-function",
    "html": "<script type='text/hyperscript'>def foo() set x to 1 then exit then set x to 2 then return x end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>def foo() set x to 1 then exit then set x to 2 then return x end</script>\")\n\t\tconst result = await evaluate(() => foo())\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "finally blocks run normally",
    "html": "<script type='text/hyperscript'>def foo()   set window.bar to 10 finally    set window.bar to 20 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   set window.bar to 10 finally    set window.bar to 20 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => foo())\n\t\tconst result = await evaluate(() => window.bar)\n\t\texpect(result).toBe(20)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "finally blocks run when an exception expr occurs",
    "html": "<script type='text/hyperscript'>def foo()   set window.bar to 10  call throwsAsyncException() finally    set window.bar to 20 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   set window.bar to 10  call throwsAsyncException() finally    set window.bar to 20 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\twindow.throwsAsyncException = function() {\n\t\t\t\treturn new Promise(function(resolve, reject) { reject(\"foo\") })\n\t\t\t}\n\t\t})\n\t\tawait evaluate(() => foo())\n\t\tawait expect.poll(() => evaluate(() => window.bar)).toBe(20)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "finally blocks run when an exception occurs",
    "html": "<script type='text/hyperscript'>def foo()   set window.bar to 10  throw \"foo\" finally    set window.bar to 20 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"def foo()   set window.bar to 10  throw \\\"foo\\\" finally    set window.bar to 20 end\" +\n\t\t\t\"</script>\"\n\t\t)\n\t\tawait evaluate(() => { try { foo() } catch(e) {} })\n\t\tconst result = await evaluate(() => window.bar)\n\t\texpect(result).toBe(20)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "functions can be namespaced",
    "html": "<script type='text/hyperscript'>def utils.foo()   add .called to #d1 end</script><div _='on click call utils.foo()'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def utils.foo()   add .called to #d1 end</script>\" +\n\t\t\t\"<div _='on click call utils.foo()'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "def",
    "name": "is called synchronously",
    "html": "<script type='text/hyperscript'>def foo()   log me end</script><div _='on click call foo() then add .called to #d1'></div><div id='d1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo()   log me end</script>\" +\n\t\t\t\"<div _='on click call foo() then add .called to #d1'></div>\" +\n\t\t\t\"<div id='d1'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "default",
    "name": "can default array elements",
    "html": "<div _=\"on click set arr to [null, null] then default arr[0] to 'yes' then put arr[0] into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click set arr to [null, null] then default arr[0] to 'yes' then put arr[0] into me\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yes\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default attributes",
    "html": "<div id='d1' _='on click default @foo to \"foo\" then put @foo into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click default @foo to \\\"foo\\\" then put @foo into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default of-expression properties",
    "html": "<div id='d1' _=\"on click default foo of me to 'bar' then put my foo into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _=\\\"on click default foo of me to 'bar' then put my foo into me\\\"></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default possessive properties",
    "html": "<div id='d1' _=\"on click default #d1's foo to 'bar' then put #d1's foo into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _=\\\"on click default #d1's foo to 'bar' then put #d1's foo into me\\\"></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default properties",
    "html": "<div id='d1' _='on click default me.foo to \"foo\" then put me.foo into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click default me.foo to \\\"foo\\\" then put me.foo into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default style ref when unset",
    "html": "<div _=\"on click default *background-color to 'red'\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click default *background-color to 'red'\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('background-color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "can default variables",
    "html": "<div id='d1' _='on click default x to \"foo\" then put x into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click default x to \\\"foo\\\" then put x into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default array element respects existing value",
    "html": "<div _=\"on click set arr to ['existing', null] then default arr[0] to 'new' then put arr[0] into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click set arr to ['existing', null] then default arr[0] to 'new' then put arr[0] into me\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"existing\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default attributes respect existing values",
    "html": "<div foo='bar' id='d1' _='on click default @foo to \"foo\" then put @foo into me'></div>",
    "body": "\n\t\tawait html(\"<div foo='bar' id='d1' _='on click default @foo to \\\"foo\\\" then put @foo into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default overwrites empty string",
    "html": "<div _='on click set x to \"\" then default x to \"fallback\" then put x into me'></div>",
    "body": "\n\t\tawait html(`<div _='on click set x to \"\" then default x to \"fallback\" then put x into me'></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"fallback\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default preserves false",
    "html": "<div _='on click set x to false then default x to true then put x into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click set x to false then default x to true then put x into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"false\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default preserves zero",
    "html": "<div _='on click set x to 0 then default x to 10 then put x into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click set x to 0 then default x to 10 then put x into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default properties respect existing values",
    "html": "<div id='d1' _='on click set me.foo to \"bar\" then default me.foo to \"foo\" then put me.foo into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set me.foo to \\\"bar\\\" then default me.foo to \\\"foo\\\" then put me.foo into me'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default style ref preserves existing value",
    "html": "<div style=\"color: blue\" _=\"on click default *color to 'red'\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div style=\"color: blue\" _=\"on click default *color to 'red'\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(0, 0, 255)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "default",
    "name": "default variables respect existing values",
    "html": "<div id='d1' _='on click set x to \"bar\" then default x to \"foo\" then put x into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set x to \\\"bar\\\" then default x to \\\"foo\\\" then put x into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "dialog",
    "name": "close closes a details element",
    "html": "<details id='d' open><summary>More</summary><p>Content</p></details><button _='on click close #d'>Close</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<details id='d' open><summary>More</summary><p>Content</p></details>\" +\n\t\t\t\"<button _='on click close #d'>Close</button>\"\n\t\t);\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "dialog",
    "name": "close closes a dialog",
    "html": "<dialog id='d'><p>Hello</p><button id='close' _='on click close #d'>Close</button></dialog>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p><button id='close' _='on click close #d'>Close</button></dialog>\"\n\t\t);\n\t\tawait evaluate(() => document.getElementById('d').showModal());\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t\tawait find('#close').click();\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "close hides a popover",
    "html": "<div id='p' popover><p>Popover content</p><button id='close' _='on click close #p'>Close</button></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='p' popover><p>Popover content</p><button id='close' _='on click close #p'>Close</button></div>\"\n\t\t);\n\t\tawait evaluate(() => document.getElementById('p').showPopover());\n\t\tawait find('#close').click();\n\t\tvar visible = await find('#p').evaluate(el => el.matches(':popover-open'));\n\t\texpect(visible).toBe(false);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "dialog",
    "name": "hide closes a dialog",
    "html": "<dialog id='d'><p>Hello</p><button id='close' _='on click hide #d'>Close</button></dialog>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p><button id='close' _='on click hide #d'>Close</button></dialog>\"\n\t\t);\n\t\tawait evaluate(() => document.getElementById('d').showModal());\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t\tawait find('#close').click();\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "open on implicit me",
    "html": "<dialog id='d' _='on myOpen open'></dialog><button _='on click send myOpen to #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d' _='on myOpen open'></dialog>\" +\n\t\t\t\"<button _='on click send myOpen to #d'>Open</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "open opens a details element",
    "html": "<details id='d'><summary>More</summary><p>Content</p></details><button _='on click open #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<details id='d'><summary>More</summary><p>Content</p></details>\" +\n\t\t\t\"<button _='on click open #d'>Open</button>\"\n\t\t);\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "dialog",
    "name": "open opens a dialog",
    "html": "<dialog id='d'><p>Hello</p></dialog><button _='on click open #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p></dialog>\" +\n\t\t\t\"<button _='on click open #d'>Open</button>\"\n\t\t);\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "open opens a modal dialog (matches :modal)",
    "html": "<dialog id='d'><p>Hello</p></dialog><button _='on click open #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p></dialog>\" +\n\t\t\t\"<button _='on click open #d'>Open</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tvar isModal = await evaluate(() => document.getElementById('d').matches(':modal'));\n\t\texpect(isModal).toBe(true);\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "open shows a popover",
    "html": "<div id='p' popover><p>Popover content</p></div><button _='on click open #p'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='p' popover><p>Popover content</p></div>\" +\n\t\t\t\"<button _='on click open #p'>Open</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tvar visible = await find('#p').evaluate(el => el.matches(':popover-open'));\n\t\texpect(visible).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "dialog",
    "name": "show on already-open dialog is a no-op",
    "html": "<dialog id='d'><p>Hello</p><button _='on click show #d'>Show Again</button></dialog>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p><button _='on click show #d'>Show Again</button></dialog>\"\n\t\t);\n\t\tawait evaluate(() => document.getElementById('d').showModal());\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "show opens a dialog (non-modal)",
    "html": "<dialog id='d'><p>Hello</p></dialog><button _='on click show #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p></dialog>\" +\n\t\t\t\"<button _='on click show #d'>Open</button>\"\n\t\t);\n\t\tawait expect(find('#d')).not.toHaveAttribute('open');\n\t\tawait find('button').click();\n\t\tawait expect(find('#d')).toHaveAttribute('open');\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "dialog",
    "name": "show opens a non-modal dialog (no ::backdrop)",
    "html": "<dialog id='d'><p>Hello</p></dialog><button _='on click show #d'>Open</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<dialog id='d'><p>Hello</p></dialog>\" +\n\t\t\t\"<button _='on click show #d'>Open</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\t// Non-modal dialogs do not match :modal; modal dialogs do.\n\t\tvar isModal = await evaluate(() => document.getElementById('d').matches(':modal'));\n\t\texpect(isModal).toBe(false);\n\t",
    "async": true,
    "complexity": "dialog"
  },
  {
    "category": "empty",
    "name": "can empty a checkbox",
    "html": "\n\t\t\t<input type=\"checkbox\" id=\"cb1\" checked />\n\t\t\t<button _=\"on click empty #cb1\">Empty</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"checkbox\" id=\"cb1\" checked />\n\t\t\t<button _=\"on click empty #cb1\">Empty</button>\n\t\t`);\n\t\tawait expect(find('#cb1')).toBeChecked();\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#cb1')).not.toBeChecked();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a form (clears all inputs)",
    "html": "\n\t\t\t<form id=\"f1\">\n\t\t\t\t<input type=\"text\" id=\"t2\" value=\"val\" />\n\t\t\t\t<textarea id=\"ta2\">text</textarea>\n\t\t\t\t<input type=\"checkbox\" id=\"cb2\" checked />\n\t\t\t</form>\n\t\t\t<button _=\"on click empty #f1\">Empty</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<form id=\"f1\">\n\t\t\t\t<input type=\"text\" id=\"t2\" value=\"val\" />\n\t\t\t\t<textarea id=\"ta2\">text</textarea>\n\t\t\t\t<input type=\"checkbox\" id=\"cb2\" checked />\n\t\t\t</form>\n\t\t\t<button _=\"on click empty #f1\">Empty</button>\n\t\t`);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#t2')).toHaveValue(\"\");\n\t\tawait expect(find('#ta2')).toHaveValue(\"\");\n\t\tawait expect(find('#cb2')).not.toBeChecked();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a map",
    "html": "<div _=\"on click\n\t\t                      set :m to {a:1, b:2} as Map\n\t\t                      empty :m\n\t\t                      put :m.size into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :m to {a:1, b:2} as Map\n\t\t                      empty :m\n\t\t                      put :m.size into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a select",
    "html": "\n\t\t\t<select id=\"sel1\">\n\t\t\t\t<option value=\"a\">A</option>\n\t\t\t\t<option value=\"b\" selected>B</option>\n\t\t\t</select>\n\t\t\t<button _=\"on click empty #sel1\">Empty</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<select id=\"sel1\">\n\t\t\t\t<option value=\"a\">A</option>\n\t\t\t\t<option value=\"b\" selected>B</option>\n\t\t\t</select>\n\t\t\t<button _=\"on click empty #sel1\">Empty</button>\n\t\t`);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar idx = await evaluate(() => document.getElementById('sel1').selectedIndex);\n\t\texpect(idx).toBe(-1);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a set",
    "html": "<div _=\"on click\n\t\t                      set :s to [1,2,3] as Set\n\t\t                      empty :s\n\t\t                      put :s.size into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :s to [1,2,3] as Set\n\t\t                      empty :s\n\t\t                      put :s.size into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a text input",
    "html": "\n\t\t\t<input type=\"text\" id=\"t1\" value=\"hello\" />\n\t\t\t<button _=\"on click empty #t1\">Empty</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"text\" id=\"t1\" value=\"hello\" />\n\t\t\t<button _=\"on click empty #t1\">Empty</button>\n\t\t`);\n\t\tawait expect(find('#t1')).toHaveValue(\"hello\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#t1')).toHaveValue(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty a textarea",
    "html": "\n\t\t\t<textarea id=\"ta1\">some text</textarea>\n\t\t\t<button _=\"on click empty #ta1\">Empty</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<textarea id=\"ta1\">some text</textarea>\n\t\t\t<button _=\"on click empty #ta1\">Empty</button>\n\t\t`);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#ta1')).toHaveValue(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty an array",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2,3]\n\t\t                      empty :arr\n\t\t                      put :arr.length into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2,3]\n\t\t                      empty :arr\n\t\t                      put :arr.length into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty an element",
    "html": "<div id='d1'><p>hello</p><p>world</p></div><button _='on click empty #d1'></button>",
    "body": "\n\t\tawait html(\"<div id='d1'><p>hello</p><p>world</p></div><button _='on click empty #d1'></button>\");\n\t\tawait expect(find('#d1')).toHaveText(\"helloworld\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "can empty multiple elements",
    "html": "<div class='clearme'><p>a</p></div><div class='clearme'><p>b</p></div><button _='on click empty .clearme'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div class='clearme'><p>a</p></div>\" +\n\t\t\t\"<div class='clearme'><p>b</p></div>\" +\n\t\t\t\"<button _='on click empty .clearme'></button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('.clearme').first()).toHaveText(\"\");\n\t\tawait expect(find('.clearme').last()).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "clear is an alias for empty",
    "html": "\n\t\t\t<input type=\"text\" id=\"t3\" value=\"hello\" />\n\t\t\t<button _=\"on click clear #t3\">Clear</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"text\" id=\"t3\" value=\"hello\" />\n\t\t\t<button _=\"on click clear #t3\">Clear</button>\n\t\t`);\n\t\tawait expect(find('#t3')).toHaveValue(\"hello\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#t3')).toHaveValue(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "clear works on elements",
    "html": "<div id='d2'><p>content</p></div><button _='on click clear #d2'></button>",
    "body": "\n\t\tawait html(\"<div id='d2'><p>content</p></div><button _='on click clear #d2'></button>\");\n\t\tawait expect(find('#d2')).toHaveText(\"content\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "empty",
    "name": "empty with no target empties me",
    "html": "<div _='on click empty'>content</div>",
    "body": "\n\t\tawait html(\"<div _='on click empty'>content</div>\");\n\t\tawait expect(find('div')).toHaveText(\"content\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can create an array literal",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[1, 2, 3]\")\n\t\texpect(result).toEqual([1, 2, 3])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of first values in an array",
    "html": "<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[..3] as String into #d1\"></div>",
    "body": "\n\t\tawait html(`<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[..3] as String into #d1\"></div>`)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"0,1,2,3\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of last values in an array",
    "html": "<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[3 ..] as String into #d1\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[3 ..] as String into #d1\"></div>`\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"3,4,5\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of last values in an array WITHOUT EXTRA SPACES",
    "html": "<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[3..] as String into #d1\"></div>",
    "body": "\n\t\tawait html(`<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[3..] as String into #d1\"></div>`)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"3,4,5\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of middle values in an array",
    "html": "<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[2 .. 3] as String into #d1\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[2 .. 3] as String into #d1\"></div>`\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"2,3\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of middle values in an array WITHOUT EXTRA SPACES",
    "html": "<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[2..3] as String into #d1\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click set var to [0,1,2,3,4,5] then put var[2..3] as String into #d1\"></div>`\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"2,3\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can get the range of middle values in an array using an expression",
    "html": "<div id=\"d1\" _=\"on click set index to 3 then set var to [0,1,2,3,4,5] then put var[(index-1)..(index+1)] as String into #d1\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click set index to 3 then set var to [0,1,2,3,4,5] then put var[(index-1)..(index+1)] as String into #d1\"></div>`\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"2,3,4\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can index an array value",
    "html": "<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"10\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can index an array value at the beginning of the array",
    "html": "<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"10\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can index an array value at the end of the array",
    "html": "<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[2] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[2] into #d1.innerHTML'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"30\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can index an array value in the middle of the array",
    "html": "<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[1] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[1] into #d1.innerHTML'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"20\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "can index an array value with an expression",
    "html": "<div id='d1' _='on click set newVar to [\"A\", \"B\", \"C\"] then put newVar[1+1] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div id=\\'d1\\' _=\\'on click set newVar to [\"A\", \"B\", \"C\"] then put newVar[1+1] into #d1.innerHTML\\'></div>'\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"C\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "errors when index exceeds array length",
    "html": "<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[10] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to [10, 20, 30] then put newVar[10] into #d1.innerHTML'></div>\"\n\t\t)\n\t\t// Original test just catches error and logs it; verify no crash\n\t\tawait find('#d1').dispatchEvent('click')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayIndex",
    "name": "errors when indexed value is not an array",
    "html": "<div id='d1' _='on click set newVar to \"not-an-array\" then put newVar[0] into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to \\\"not-an-array\\\" then put newVar[0] into #d1.innerHTML'></div>\"\n\t\t)\n\t\t// Original test just catches error and logs it; verify no crash\n\t\tawait find('#d1').dispatchEvent('click')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "arrays can contain expressions",
    "html": "",
    "body": "\n\t\texpect(await run(\"[1 + 1, 2 * 3, 10 - 5]\")).toEqual([2, 6, 5])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "arrays containing objects work",
    "html": "",
    "body": "\n\t\texpect(await run(\"[{a: 1}, {b: 2}]\")).toEqual([{a: 1}, {b: 2}])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "deeply nested array literals work",
    "html": "",
    "body": "\n\t\texpect(await run(\"[[[1]], [[2, 3]]]\")).toEqual([[[1]], [[2, 3]]])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "empty array literals work",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[]\")\n\t\texpect(result).toEqual([])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "mixed-type array literal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"[1, 'two', true, null]\")).toEqual([1, 'two', true, null])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "multi element array literal works",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[true, false]\")\n\t\texpect(result).toEqual([true, false])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "nested array literals work",
    "html": "",
    "body": "\n\t\texpect(await run(\"[[1, 2], [3, 4]]\")).toEqual([[1, 2], [3, 4]])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/arrayLiteral",
    "name": "one element array literal works",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[true]\")\n\t\texpect(result).toEqual([true])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "can accept custom conversions",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\t_hyperscript.config.conversions[\"Foo\"] = function (val) {\n\t\t\t\treturn \"foo\" + val\n\t\t\t}\n\t\t\tconst r = _hyperscript(\"1 as Foo\")\n\t\t\tdelete _hyperscript.config.conversions.Foo\n\t\t\treturn r\n\t\t})\n\t\texpect(result).toBe(\"foo1\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "can accept custom dynamic conversions",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst myConversion = function (conversion, val) {\n\t\t\t\tif (conversion.indexOf(\"Foo:\") === 0) {\n\t\t\t\t\tconst arg = conversion.split(\":\")[1]\n\t\t\t\t\treturn arg + val\n\t\t\t\t}\n\t\t\t}\n\t\t\t_hyperscript.config.conversions.dynamicResolvers.push(myConversion)\n\t\t\tconst r = _hyperscript(\"1 as Foo:Bar\")\n\t\t\t_hyperscript.config.conversions.dynamicResolvers.pop()\n\t\t\treturn r\n\t\t})\n\t\texpect(result).toBe(\"Bar1\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "can use the a modifier if you like",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"1 as a Date\")\n\t\t\treturn r.getTime()\n\t\t})\n\t\texpect(result).toBe(new Date(1).getTime())\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "can use the an modifier if you'd like",
    "html": "",
    "body": "\n\t\tconst result = await run('\\'{\"foo\":\"bar\"}\\' as an Object')\n\t\texpect(result[\"foo\"]).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "collects duplicate text inputs into an array",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<input name=\"tag\" value=\"alpha\">\n\t\t\t\t<input name=\"tag\" value=\"beta\">\n\t\t\t\t<input name=\"tag\" value=\"gamma\">`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.tag).toEqual([\"alpha\", \"beta\", \"gamma\"])\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a NodeList into HTML",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst fragment = document.createDocumentFragment()\n\t\t\tlet d = document.createElement(\"div\")\n\t\t\td.id = \"first\"\n\t\t\td.innerText = \"With Text\"\n\t\t\tfragment.appendChild(d)\n\t\t\td = document.createElement(\"span\")\n\t\t\td.id = \"second\"\n\t\t\tfragment.appendChild(d)\n\t\t\td = document.createElement(\"i\")\n\t\t\td.id = \"third\"\n\t\t\tfragment.appendChild(d)\n\t\t\treturn _hyperscript(\"nodeList as HTML\", { locals: { nodeList: fragment.childNodes } })\n\t\t})\n\t\texpect(result).toBe(`<div id=\"first\">With Text</div><span id=\"second\"></span><i id=\"third\"></i>`)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a complete form into Values",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<div><span><b>\n\t\t\t\t\tCatches elements nested deeply within the DOM tree\n\t\t\t\t\t<input name=\"firstName\" value=\"John\"><br>\n\t\t\t\t\t<input name=\"lastName\" value=\"Connor\"><br>\n\t\t\t\t\t<input name=\"phone\" value=\"555-1212\">\n\t\t\t\t</b></span></div>\n\t\t\t\tWorks with Textareas\n\t\t\t\t<textarea name=\"aboutMe\">It began on a warm summer day in 1969...</textarea>\n\t\t\t\tWorks with Single Select Boxes\n\t\t\t\t<select name=\"animal\">\n\t\t\t\t\t<option value=\"dog\" selected>Doggo</option>\n\t\t\t\t\t<option value=\"cat\">Kitteh</option>\n\t\t\t\t\t<option value=\"raccoon\">Trash Panda</option>\n\t\t\t\t\t<option value=\"possum\">Sleepy Boi</option>\n\t\t\t\t</select>\n\t\t\t\tWorks with Multi-Select Boxes\n\t\t\t\t<select name=\"spiritAnimal\" multiple>\n\t\t\t\t\t<option value=\"dog\" selected>Doggo</option>\n\t\t\t\t\t<option value=\"cat\">Kitteh</option>\n\t\t\t\t\t<option value=\"raccoon\" selected>Trash Panda</option>\n\t\t\t\t\t<option value=\"possum\">Sleepy Boi</option>\n\t\t\t\t</select>\n\t\t\t\tWorks with Radio Buttons\n\t\t\t\t<input type=\"radio\" name=\"coolOrNaw\" value=\"Cool\" checked>\n\t\t\t\t<input type=\"radio\" name=\"coolOrNaw\" value=\"Naw Bruh\">\n\t\t\t\tWorks with Checkboxes\n\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Male\" checked>\n\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Female\" checked>\n\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Other\" checked>`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.firstName).toBe(\"John\")\n\t\texpect(result.lastName).toBe(\"Connor\")\n\t\texpect(result.phone).toBe(\"555-1212\")\n\t\texpect(result.aboutMe).toBe(\"It began on a warm summer day in 1969...\")\n\t\texpect(result.animal).toBe(\"dog\")\n\t\texpect(result.spiritAnimal[0]).toBe(\"dog\")\n\t\texpect(result.spiritAnimal[1]).toBe(\"raccoon\")\n\t\texpect(result.coolOrNaw).toBe(\"Cool\")\n\t\texpect(result.gender[0]).toBe(\"Male\")\n\t\texpect(result.gender[1]).toBe(\"Female\")\n\t\texpect(result.gender[2]).toBe(\"Other\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a form element into Values",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<input name=\"firstName\" value=\"John\"><br>\n\t\t\t\t<input name=\"lastName\" value=\"Connor\"><br>\n\t\t\t\t<div>\n\t\t\t\t\t<input name=\"areaCode\" value=\"213\">\n\t\t\t\t\t<input name=\"phone\" value=\"555-1212\">\n\t\t\t\t</div>`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.firstName).toBe(\"John\")\n\t\texpect(result.lastName).toBe(\"Connor\")\n\t\texpect(result.areaCode).toBe(\"213\")\n\t\texpect(result.phone).toBe(\"555-1212\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a form element into Values | FormEncoded",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<input name=\"firstName\" value=\"John\"><br>\n\t\t\t\t<input name=\"lastName\" value=\"Connor\"><br>\n\t\t\t\t<div>\n\t\t\t\t\t<input name=\"areaCode\" value=\"213\">\n\t\t\t\t\t<input name=\"phone\" value=\"555-1212\">\n\t\t\t\t</div>`\n\t\t\treturn _hyperscript(\"x as Values | FormEncoded\", { locals: { x: node } })\n\t\t})\n\t\texpect(result).toBe('firstName=John&lastName=Connor&areaCode=213&phone=555-1212')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a form element into Values | JSONString",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<input name=\"firstName\" value=\"John\"><br>\n\t\t\t\t<input name=\"lastName\" value=\"Connor\"><br>\n\t\t\t\t<div>\n\t\t\t\t\t<input name=\"areaCode\" value=\"213\">\n\t\t\t\t\t<input name=\"phone\" value=\"555-1212\">\n\t\t\t\t</div>`\n\t\t\treturn _hyperscript(\"x as Values | JSONString\", { locals: { x: node } })\n\t\t})\n\t\texpect(result).toBe('{\"firstName\":\"John\",\"lastName\":\"Connor\",\"areaCode\":\"213\",\"phone\":\"555-1212\"}')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts a query selector into Values",
    "html": "<div id=\"qsdiv\" _=\"on click put <input.include/> as Values into my.customData\">\n\t\t\t<input class=\"include\" name=\"firstName\" value=\"John\"><br>\n\t\t\t<input class=\"include\" name=\"lastName\" value=\"Connor\"><br>\n\t\t\t<input class=\"include\" name=\"areaCode\" value=\"213\">\n\t\t\t<input class=\"dont-include\" name=\"phone\" value=\"555-1212\">\n\t\t</div>",
    "body": "\n\t\tawait html(`<div id=\"qsdiv\" _=\"on click put <input.include/> as Values into my.customData\">\n\t\t\t<input class=\"include\" name=\"firstName\" value=\"John\"><br>\n\t\t\t<input class=\"include\" name=\"lastName\" value=\"Connor\"><br>\n\t\t\t<input class=\"include\" name=\"areaCode\" value=\"213\">\n\t\t\t<input class=\"dont-include\" name=\"phone\" value=\"555-1212\">\n\t\t</div>`)\n\t\tawait find('#qsdiv').dispatchEvent('click')\n\t\tconst data = await evaluate(() => {\n\t\t\tconst div = document.getElementById('qsdiv')\n\t\t\treturn {\n\t\t\t\tfirstName: div.customData?.firstName,\n\t\t\t\tlastName: div.customData?.lastName,\n\t\t\t\tareaCode: div.customData?.areaCode,\n\t\t\t\tphone: div.customData?.phone,\n\t\t\t}\n\t\t})\n\t\texpect(data.firstName).toBe(\"John\")\n\t\texpect(data.lastName).toBe(\"Connor\")\n\t\texpect(data.areaCode).toBe(\"213\")\n\t\texpect(data.phone).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts an array into HTML",
    "html": "",
    "body": "\n\t\tconst result = await run(\"d as HTML\", { locals: { d: [\"this-\", \"is-\", \"html\"] } })\n\t\texpect(result).toBe(`this-is-html`)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts an element into HTML",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst d1 = document.createElement(\"div\")\n\t\t\td1.id = \"myDiv\"\n\t\t\td1.innerText = \"With Text\"\n\t\t\treturn _hyperscript(\"d as HTML\", { locals: { d: d1 } })\n\t\t})\n\t\texpect(result).toBe(`<div id=\"myDiv\">With Text</div>`)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts an input element into Values",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"input\")\n\t\t\tnode.name = \"test-name\"\n\t\t\tnode.value = \"test-value\"\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result[\"test-name\"]).toBe(\"test-value\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts array as Reversed",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[1,2,3] as Reversed\", {})\n\t\texpect(result).toEqual([3, 2, 1])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts array as Set",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"[1,2,2,3] as Set\")\n\t\t\treturn { isSet: r instanceof Set, size: r.size }\n\t\t})\n\t\texpect(result.isSet).toBe(true)\n\t\texpect(result.size).toBe(3)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts array as Unique",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[1,2,2,3,3] as Unique\", {})\n\t\texpect(result).toEqual([1, 2, 3])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts arrays into fragments",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = [document.createElement(\"p\"), \"<p></p>\"]\n\t\t\tconst r = _hyperscript(\"value as Fragment\", { locals: { value } })\n\t\t\treturn {\n\t\t\t\tchildElementCount: r.childElementCount,\n\t\t\t\tfirstChildTag: r.firstChild.tagName,\n\t\t\t\tlastChildTag: r.lastChild.tagName,\n\t\t\t}\n\t\t})\n\t\texpect(result.childElementCount).toBe(2)\n\t\texpect(result.firstChildTag).toBe(\"P\")\n\t\texpect(result.lastChildTag).toBe(\"P\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts checkboxes into a Value correctly",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<div>\n\t\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Male\" checked>\n\t\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Female\" checked>\n\t\t\t\t\t<input type=\"checkbox\" name=\"gender\" value=\"Other\" checked>\n\t\t\t\t</div>`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.gender[0]).toBe(\"Male\")\n\t\texpect(result.gender[1]).toBe(\"Female\")\n\t\texpect(result.gender[2]).toBe(\"Other\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts elements into fragments",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = document.createElement(\"p\")\n\t\t\tconst r = _hyperscript(\"value as Fragment\", { locals: { value } })\n\t\t\treturn { childElementCount: r.childElementCount, firstChildTag: r.firstChild.tagName }\n\t\t})\n\t\texpect(result.childElementCount).toBe(1)\n\t\texpect(result.firstChildTag).toBe(\"P\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts multiple selects into a Value correctly",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<select name=\"animal\" multiple>\n\t\t\t\t\t<option value=\"dog\" selected>Doggo</option>\n\t\t\t\t\t<option value=\"cat\">Kitteh</option>\n\t\t\t\t\t<option value=\"raccoon\" selected>Trash Panda</option>\n\t\t\t\t\t<option value=\"possum\">Sleepy Boi</option>\n\t\t\t\t</select>`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.animal[0]).toBe(\"dog\")\n\t\texpect(result.animal[1]).toBe(\"raccoon\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts multiple selects with programmatically changed selections",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<select name=\"animal\" multiple>\n\t\t\t\t\t<option value=\"dog\" selected>Doggo</option>\n\t\t\t\t\t<option value=\"cat\">Kitteh</option>\n\t\t\t\t\t<option value=\"raccoon\" selected>Trash Panda</option>\n\t\t\t\t\t<option value=\"possum\">Sleepy Boi</option>\n\t\t\t\t</select>`\n\t\t\tvar select = node.querySelector(\"select\")\n\t\t\tselect.options[0].selected = false   // deselect dog\n\t\t\tselect.options[1].selected = true     // select cat\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.animal[0]).toBe(\"cat\")\n\t\texpect(result.animal[1]).toBe(\"raccoon\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts nested array as Flat",
    "html": "",
    "body": "\n\t\tconst result = await run(\"[[1,2],[3,4]] as Flat\", {})\n\t\texpect(result).toEqual([1, 2, 3, 4])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts null as null",
    "html": "",
    "body": "\n\t\tconst result = await run(\"null as String\")\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts numbers things 'HTML'",
    "html": "",
    "body": "\n\t\tconst result = await run(\"value as HTML\", { locals: { value: 123 } })\n\t\texpect(result).toBe(\"123\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts object as Entries",
    "html": "",
    "body": "\n\t\tconst result = await run(\"{a:1} as Entries\", {})\n\t\texpect(result).toEqual([[\"a\", 1]])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts object as Keys",
    "html": "",
    "body": "\n\t\tconst result = await run(\"{a:1, b:2} as Keys\", {})\n\t\texpect(result).toEqual([\"a\", \"b\"])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts object as Map",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"{a:1, b:2} as Map\")\n\t\t\treturn { isMap: r instanceof Map, a: r.get(\"a\"), size: r.size }\n\t\t})\n\t\texpect(result.isMap).toBe(true)\n\t\texpect(result.a).toBe(1)\n\t\texpect(result.size).toBe(2)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts radio buttons into a Value correctly",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst node = document.createElement(\"form\")\n\t\t\tnode.innerHTML = `\n\t\t\t\t<div>\n\t\t\t\t\t<input type=\"radio\" name=\"gender\" value=\"Male\" checked>\n\t\t\t\t\t<input type=\"radio\" name=\"gender\" value=\"Female\">\n\t\t\t\t\t<input type=\"radio\" name=\"gender\" value=\"Other\">\n\t\t\t\t</div>`\n\t\t\treturn _hyperscript(\"x as Values\", { locals: { x: node } })\n\t\t})\n\t\texpect(result.gender).toBe(\"Male\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts string as Object",
    "html": "",
    "body": "\n\t\tconst result = await run('\\'{\"foo\":\"bar\"}\\' as Object')\n\t\texpect(result[\"foo\"]).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts strings into fragments",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"value as Fragment\", { locals: { value: \"<p></p>\" } })\n\t\t\treturn { childElementCount: r.childElementCount, firstChildTag: r.firstChild.tagName }\n\t\t})\n\t\texpect(result.childElementCount).toBe(1)\n\t\texpect(result.firstChildTag).toBe(\"P\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Boolean",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 as Boolean\")).toBe(true)\n\t\texpect(await run(\"0 as Boolean\")).toBe(false)\n\t\texpect(await run(\"'' as Boolean\")).toBe(false)\n\t\texpect(await run(\"'hello' as Boolean\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Date",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"1 as Date\")\n\t\t\treturn r.getTime()\n\t\t})\n\t\texpect(result).toBe(new Date(1).getTime())\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Fixed",
    "html": "",
    "body": "\n\t\tlet result = await run(\"'10.4' as Fixed\")\n\t\texpect(result).toBe('10')\n\t\tresult = await run(\"'10.4899' as Fixed:2\")\n\t\texpect(result).toBe('10.49')\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Float",
    "html": "",
    "body": "\n\t\tlet result = await run(\"'10' as Float\")\n\t\texpect(result).toBe(10)\n\t\tresult = await run(\"'10.4' as Float\")\n\t\texpect(result).toBe(10.4)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Int",
    "html": "",
    "body": "\n\t\tlet result = await run(\"'10' as Int\")\n\t\texpect(result).toBe(10)\n\t\tresult = await run(\"'10.4' as Int\")\n\t\texpect(result).toBe(10)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as JSONString",
    "html": "",
    "body": "\n\t\tconst result = await run(\"{foo:'bar'} as JSONString\")\n\t\texpect(result).toBe('{\"foo\":\"bar\"}')\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Number",
    "html": "",
    "body": "\n\t\tlet result = await run(\"'10' as Number\")\n\t\texpect(result).toBe(10)\n\t\tresult = await run(\"'10.4' as Number\")\n\t\texpect(result).toBe(10.4)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as Object",
    "html": "",
    "body": "\n\t\tconst result = await run(\"x as Object\", { locals: { x: { foo: \"bar\" } } })\n\t\texpect(result[\"foo\"]).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "converts value as String",
    "html": "",
    "body": "\n\t\tlet result = await run(\"10 as String\")\n\t\texpect(result).toBe(\"10\")\n\t\tresult = await run(\"true as String\")\n\t\texpect(result).toBe(\"true\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "parses string as JSON to object",
    "html": "",
    "body": "\n\t\tconst result = await run('\\'{\"foo\":\"bar\"}\\' as JSON')\n\t\texpect(result[\"foo\"]).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/asExpression",
    "name": "pipe operator chains conversions",
    "html": "",
    "body": "\n\t\tconst result = await run(\"{foo:'bar'} as JSONString | JSON\")\n\t\texpect(result[\"foo\"]).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/assignableElements",
    "name": "hyperscript in replacement content is initialized",
    "html": "<div id='target'>old</div><button id='go' _=\"on click set #target to '<div id=target _=&#34;on click put `clicked` into me&#34;>new</div>'\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button id='go' _=\\\"on click set #target to '<div id=target _=&#34;on click put `clicked` into me&#34;>new</div>'\\\">go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"new\");\n\t\tawait find('#target').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "put into still works as innerHTML",
    "html": "<div id='target'>old</div><button _='on click put \"new\" into #target'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button _='on click put \\\"new\\\" into #target'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"new\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "set #id replaces element with HTML string",
    "html": "<div id='target'>old</div><button _='on click set #target to \"<span id=target>new</span>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button _='on click set #target to \\\"<span id=target>new</span>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"new\");\n\t\tvar tag = await evaluate(() => document.querySelector('#target').tagName);\n\t\texpect(tag).toBe(\"SPAN\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "set #id replaces element with another element",
    "html": "<div id='target'>old</div><button _='on click make a <span.replaced/> then put \"moved\" into it then set #target to it'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button _='on click make a <span.replaced/> then put \\\"moved\\\" into it then set #target to it'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar text = await evaluate(() => document.querySelector('.replaced').textContent);\n\t\texpect(text).toBe(\"moved\");\n\t\tvar gone = await evaluate(() => document.querySelector('#target'));\n\t\texpect(gone).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "set .class replaces all matching elements",
    "html": "<ul id='list'>  <li class='item'>a</li>  <li class='item'>b</li>  <li class='item'>c</li></ul><button _='on click set .item to \"<li class=item>replaced</li>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<ul id='list'>\" +\n\t\t\t\"  <li class='item'>a</li>\" +\n\t\t\t\"  <li class='item'>b</li>\" +\n\t\t\t\"  <li class='item'>c</li>\" +\n\t\t\t\"</ul>\" +\n\t\t\t\"<button _='on click set .item to \\\"<li class=item>replaced</li>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar count = await evaluate(() => document.querySelectorAll('.item').length);\n\t\texpect(count).toBe(3);\n\t\tvar texts = await evaluate(() => Array.from(document.querySelectorAll('.item')).map(e => e.textContent));\n\t\texpect(texts).toEqual([\"replaced\", \"replaced\", \"replaced\"]);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "set <query/> replaces all matching elements",
    "html": "<div id='box'>  <p>one</p>  <p>two</p></div><button _='on click set <p/> in #box to \"<p>done</p>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box'>\" +\n\t\t\t\"  <p>one</p>\" +\n\t\t\t\"  <p>two</p>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button _='on click set <p/> in #box to \\\"<p>done</p>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar texts = await evaluate(() => Array.from(document.querySelectorAll('#box p')).map(e => e.textContent));\n\t\texpect(texts).toEqual([\"done\", \"done\"]);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "set closest replaces ancestor",
    "html": "<div class='wrapper'><button _='on click set (closest <div/>) to \"<div class=wrapper>replaced</div>\"'>go</button></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div class='wrapper'><button _='on click set (closest <div/>) to \\\"<div class=wrapper>replaced</div>\\\"'>go</button></div>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('.wrapper')).toHaveText(\"replaced\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/assignableElements",
    "name": "swap #a with #b swaps DOM positions",
    "html": "<div id='container'>  <div id='a'>A</div>  <div id='b'>B</div></div><button _='on click swap #a with #b'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='container'>\" +\n\t\t\t\"  <div id='a'>A</div>\" +\n\t\t\t\"  <div id='b'>B</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button _='on click swap #a with #b'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar order = await evaluate(() =>\n\t\t\tArray.from(document.querySelector('#container').children).map(e => e.textContent.trim())\n\t\t);\n\t\texpect(order).toEqual([\"B\", \"A\"]);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be put as symbol",
    "html": "<div id='arDiv' _='on click put \"blue\" into [@data-foo]' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click put \\\"blue\\\" into [@data-foo]' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be put as symbol w/ short syntax",
    "html": "<div id='arDiv' _='on click put \"blue\" into @data-foo' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click put \\\"blue\\\" into @data-foo' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be put indirectly",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"put 'blue' into x[@data-foo]\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be put indirectly w/ short syntax",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"put 'blue' into x@data-foo\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set as prop",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"set x[@data-foo] to 'blue'\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set as prop w/ short syntax",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"set x@data-foo to 'blue'\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set as symbol",
    "html": "<div id='arDiv' _='on click set [@data-foo] to \"blue\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click set [@data-foo] to \\\"blue\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set as symbol w/ short syntax",
    "html": "<div id='arDiv' _='on click set @data-foo to \"blue\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click set @data-foo to \\\"blue\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set indirectly",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"set [@data-foo] of x to 'blue'\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set indirectly w/ short syntax",
    "html": "<div id='arDiv' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('arDiv')\n\t\t\t_hyperscript(\"set @data-foo of x to 'blue'\", { locals: { x: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set through possessive",
    "html": "<div id='arDiv' _='on click set my [@data-foo] to \"blue\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click set my [@data-foo] to \\\"blue\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can be set through possessive w/ short syntax",
    "html": "<div id='arDiv' _='on click set my @data-foo to \"blue\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click set my @data-foo to \\\"blue\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value in quotes used in add commands",
    "html": "<div id='arDiv' _='on click add [@data-foo=\"blue\"]' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add [@data-foo=\\\"blue\\\"]' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value in quotes used in add commands w/ short syntax",
    "html": "<div id='arDiv' _='on click add @data-foo=\"blue\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add @data-foo=\\\"blue\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value in quotes with spaces used in add commands",
    "html": "<div id='arDiv' _='on click add [@data-foo=\"blue green\"]' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add [@data-foo=\\\"blue green\\\"]' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue green\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value in quotes with spaces used in add commands w/ short syntax",
    "html": "<div id='arDiv' _='on click add @data-foo=\"blue green\"' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add @data-foo=\\\"blue green\\\"' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue green\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value used in add commands",
    "html": "<div id='arDiv' _='on click add [@data-foo=blue]' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add [@data-foo=blue]' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef can have value used in add commands w/ short syntax",
    "html": "<div id='arDiv' _='on click add @data-foo=blue' data-foo='red'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' _='on click add @data-foo=blue' data-foo='red'></div>\")\n\t\tawait find('#arDiv').dispatchEvent('click')\n\t\tconst value = await evaluate(() => document.getElementById('arDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef with dashes name works",
    "html": "<div id='arDiv' data-foo='c1'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='c1'></div>\")\n\t\tconst value = await evaluate(() => _hyperscript(\"[@data-foo]\", { me: document.getElementById('arDiv') }))\n\t\texpect(value).toBe(\"c1\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef with dashes name works w/ short syntax",
    "html": "<div id='arDiv' data-foo='c1'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' data-foo='c1'></div>\")\n\t\tconst value = await evaluate(() => _hyperscript(\"@data-foo\", { me: document.getElementById('arDiv') }))\n\t\texpect(value).toBe(\"c1\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef with no value works",
    "html": "<div id='arDiv' foo='c1'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' foo='c1'></div>\")\n\t\tconst value = await evaluate(() => _hyperscript(\"[@foo]\", { me: document.getElementById('arDiv') }))\n\t\texpect(value).toBe(\"c1\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/attributeRef",
    "name": "attributeRef with no value works w/ short syntax",
    "html": "<div id='arDiv' foo='c1'></div>",
    "body": "\n\t\tawait html(\"<div id='arDiv' foo='c1'></div>\")\n\t\tconst value = await evaluate(() => _hyperscript(\"@foo\", { me: document.getElementById('arDiv') }))\n\t\texpect(value).toBe(\"c1\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/beep!",
    "name": "beeps a basic value",
    "html": "<div _='on click get beep! 10'></div>",
    "body": "\n\t\tconst logs = []\n\t\tpage.on('console', async msg => {\n\t\t\tif (msg.type() === 'log') {\n\t\t\t\tlogs.push(await Promise.all(msg.args().map(a => a.jsonValue())))\n\t\t\t}\n\t\t})\n\t\tawait html(\"<div _='on click get beep! 10'></div>\")\n\t\tawait page.locator('#work-area div').dispatchEvent('click')\n\t\tawait expect.poll(() => logs.length).toBeGreaterThan(0)\n\t\texpect(logs[0]).toEqual(['///_ BEEP! The expression (10) evaluates to:', 10, 'of type Number'])\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/beep!",
    "name": "beeps a formatted string value",
    "html": "<div _='on click get beep! \"foo\"'></div>",
    "body": "\n\t\tconst logs = []\n\t\tpage.on('console', async msg => {\n\t\t\tif (msg.type() === 'log') {\n\t\t\t\tlogs.push(await Promise.all(msg.args().map(a => a.jsonValue())))\n\t\t\t}\n\t\t})\n\t\tawait html(\"<div _='on click get beep! \\\"foo\\\"'></div>\")\n\t\tawait page.locator('#work-area div').dispatchEvent('click')\n\t\tawait expect.poll(() => logs.length).toBeGreaterThan(0)\n\t\texpect(logs[0]).toEqual(['///_ BEEP! The expression (\"foo\") evaluates to:', '\"foo\"', 'of type String'])\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/beep!",
    "name": "beeps a null value",
    "html": "<div _='on click get beep! null'></div>",
    "body": "\n\t\tconst logs = []\n\t\tpage.on('console', async msg => {\n\t\t\tif (msg.type() === 'log') {\n\t\t\t\tlogs.push(await Promise.all(msg.args().map(a => a.jsonValue())))\n\t\t\t}\n\t\t})\n\t\tawait html(\"<div _='on click get beep! null'></div>\")\n\t\tawait page.locator('#work-area div').dispatchEvent('click')\n\t\tawait expect.poll(() => logs.length).toBeGreaterThan(0)\n\t\texpect(logs[0]).toEqual(['///_ BEEP! The expression (null) evaluates to:', null, 'of type object (null)'])\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/beep!",
    "name": "beeps the result of an ElementCollection",
    "html": "<div class='foo' _='on click get beep! .foo'></div>",
    "body": "\n\t\tconst logs = []\n\t\tpage.on('console', async msg => {\n\t\t\tif (msg.type() === 'log') {\n\t\t\t\tlogs.push(await Promise.all(msg.args().map(a => a.jsonValue().catch(() => '[Element]'))))\n\t\t\t}\n\t\t})\n\t\tawait html(\"<div class='foo' _='on click get beep! .foo'></div>\")\n\t\tawait page.locator('#work-area div.foo').dispatchEvent('click')\n\t\tawait expect.poll(() => logs.length).toBeGreaterThan(0)\n\t\t// The first two entries are the message and an element (which serializes as an object)\n\t\texpect(logs[0][0]).toBe('///_ BEEP! The expression (.foo) evaluates to:')\n\t\texpect(logs[0][2]).toBe('of type ElementCollection')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/beep!",
    "name": "can be cancelled",
    "html": "<div _='on hyperscript:beep halt on click get beep! \"foo\"'></div>",
    "body": "\n\t\tconst logs = []\n\t\tpage.on('console', async msg => {\n\t\t\tif (msg.type() === 'log') {\n\t\t\t\tconst text = msg.text()\n\t\t\t\tif (text.includes('BEEP')) {\n\t\t\t\t\tlogs.push(text)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tawait html(\"<div _='on hyperscript:beep halt on click get beep! \\\"foo\\\"'></div>\")\n\t\tawait page.locator('#work-area div').dispatchEvent('click')\n\t\t// Wait a bit to ensure no beep logs appear\n\t\tawait page.waitForTimeout(100)\n\t\texpect(logs.length).toBe(0)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/beep!",
    "name": "can capture information from event",
    "html": "<div id='beepDiv' _='on hyperscript:beep(value) set my @data-value to the value on click get beep! \"foo\"'></div>",
    "body": "\n\t\tawait html(\"<div id='beepDiv' _='on hyperscript:beep(value) set my @data-value to the value on click get beep! \\\"foo\\\"'></div>\")\n\t\tawait find('#beepDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('beepDiv').getAttribute(\"data-value\"))).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/blockLiteral",
    "name": "basic block literals work",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst fn = _hyperscript(\"\\\\-> true\")\n\t\t\treturn fn()\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/blockLiteral",
    "name": "basic identity works",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst fn = _hyperscript(\"\\\\ x -> x\")\n\t\t\treturn fn(true)\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/blockLiteral",
    "name": "basic two arg identity works",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst fn = _hyperscript(\"\\\\ x, y -> y\")\n\t\t\treturn fn(false, true)\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/blockLiteral",
    "name": "can map an array",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"['a', 'ab', 'abc'].map(\\\\ s -> s.length )\")\n\t\t})\n\t\texpect(result).toEqual([1, 2, 3])\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/boolean",
    "name": "false boolean literals work",
    "html": "",
    "body": "\n\t\tconst result = await run(\"false\")\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/boolean",
    "name": "true boolean literals work",
    "html": "",
    "body": "\n\t\tconst result = await run(\"true\")\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/classRef",
    "name": "basic classRef works",
    "html": "<div class='c1'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".c1\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "basic classRef works w no match",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".badClassThatDoesNotHaveAnyElements\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(0)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/classRef",
    "name": "colon class ref works",
    "html": "<div class='c1:foo'></div>",
    "body": "\n\t\tawait html(\"<div class='c1:foo'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".c1:foo\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "dashed class ref works",
    "html": "<div class='c1-foo'></div>",
    "body": "\n\t\tawait html(\"<div class='c1-foo'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".c1-foo\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "leading minus class ref works",
    "html": "<div class='-c1'></div>",
    "body": "\n\t\tawait html(\"<div class='-c1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".-c1\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "multiple colon class ref works",
    "html": "<div class='c1:foo:bar'></div>",
    "body": "\n\t\tawait html(\"<div class='c1:foo:bar'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".c1:foo:bar\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "slashes in class references work",
    "html": "<div class='-c1/22'></div>",
    "body": "\n\t\tawait html(\"<div class='-c1/22'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".-c1\\\\/22\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "tailwind insanity in class references work",
    "html": "<div class='group-[:nth-of-type(3)_&]:block'></div>",
    "body": "\n\t\tawait html(\"<div class='group-[:nth-of-type(3)_&]:block'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".group-\\\\[:nth-of-type\\\\(3\\\\)_\\\\&\\\\]:block\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/classRef",
    "name": "template classRef works",
    "html": "<div class='c1'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\".{'c1'}\")\n\t\t\treturn Array.from(value).length\n\t\t})\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "attributes can be looked up and referred to in same expression",
    "html": "<div foo='bar'><div id='d1' _='on click put closest @foo into me'></div></div>",
    "body": "\n\t\tawait html(\"<div foo='bar'><div id='d1' _='on click put closest @foo into me'></div></div>\")\n\t\tawait expect(find('#d1')).toHaveText(\"\")\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText(\"bar\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "attributes can be set via the closest expression",
    "html": "<div id='outerDiv' foo='bar'><div id='d1' _='on click set closest @foo to \"doh\"'></div></div>",
    "body": "\n\t\tawait html(\"<div id='outerDiv' foo='bar'><div id='d1' _='on click set closest @foo to \\\"doh\\\"'></div></div>\")\n\t\tlet value = await evaluate(() => document.getElementById('outerDiv').getAttribute(\"foo\"))\n\t\texpect(value).toBe(\"bar\")\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('outerDiv').getAttribute(\"foo\"))).toBe(\"doh\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "attributes can be set via the closest expression 2",
    "html": "<div id='outerDiv2' foo='bar'><div id='d1b' _='on click set closest @foo to \"doh\"'></div></div>",
    "body": "\n\t\tawait html(\"<div id='outerDiv2' foo='bar'><div id='d1b' _='on click set closest @foo to \\\"doh\\\"'></div></div>\")\n\t\tlet value = await evaluate(() => document.getElementById('outerDiv2').getAttribute(\"foo\"))\n\t\texpect(value).toBe(\"bar\")\n\t\tawait find('#d1b').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('outerDiv2').getAttribute(\"foo\"))).toBe(\"doh\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "attributes resolve as attributes",
    "html": "<div foo='bar' id='d3'><div id='d1'></div><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\"<div foo='bar' id='d3'><div id='d1'></div><div id='d2'></div></div>\")\n\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\treturn _hyperscript(\"closest @foo\", { me: div1 })\n\t\t})\n\t\texpect(result).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "basic query return values",
    "html": "<div id='d3'><div id='d1'></div><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\"<div id='d3'><div id='d1'></div><div id='d2'></div></div>\")\n\n\t\tlet result = await evaluate(() => {\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest <div/>\", { me: div3 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\treturn _hyperscript(\"closest <div/>\", { me: div1 }) === div1\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest <div/> to #d3\", { me: div1 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest <div/> to my.parentElement\", { me: div1 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest <div/> to parentElement of me\", { me: div1 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "closest does not consume a following where clause",
    "html": "<table><tr><td><input type='checkbox' class='cb'><input type='checkbox' class='cb'><input id='master' type='checkbox' _=\"set :others to <input[type=checkbox]/> in the closest <table/> where it is not me on click put :others.length into #out\"></td></tr></table><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<table><tr><td>\" +\n\t\t\t\"<input type='checkbox' class='cb'>\" +\n\t\t\t\"<input type='checkbox' class='cb'>\" +\n\t\t\t\"<input id='master' type='checkbox' \" +\n\t\t\t\"_=\\\"set :others to <input[type=checkbox]/> in the closest <table/> where it is not me \" +\n\t\t\t\"on click put :others.length into #out\\\">\" +\n\t\t\t\"</td></tr></table>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\tawait find('#master').click();\n\t\tawait expect(find('#out')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "closest with to modifier still works after parse change",
    "html": "<div id='outer'><div id='inner'></div></div>",
    "body": "\n\t\tawait html(\"<div id='outer'><div id='inner'></div></div>\")\n\t\tlet result = await evaluate(() => {\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\treturn _hyperscript(\"closest <div/> to my.parentElement\", { me: inner }) === outer\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "parent modifier works",
    "html": "<div id='d3'><div id='d1'></div><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\"<div id='d3'><div id='d1'></div><div id='d2'></div></div>\")\n\n\t\tlet result = await evaluate(() => {\n\t\t\tconst div1 = document.getElementById('d1')\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest parent <div/>\", { me: div1 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div2 = document.getElementById('d2')\n\t\t\tconst div3 = document.getElementById('d3')\n\t\t\treturn _hyperscript(\"closest parent <div/>\", { me: div2 }) === div3\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "parenthesizing allows you to nest to modifiers properly",
    "html": "<div id='outerDiv' foo='bar'><div id='d1'></div></div><div id='div2' _='on click set (closest @foo to #d1) to \"doh\"'></div>",
    "body": "\n\t\tawait html(\"<div id='outerDiv' foo='bar'><div id='d1'></div></div><div id='div2' _='on click set (closest @foo to #d1) to \\\"doh\\\"'></div>\")\n\t\tlet value = await evaluate(() => document.getElementById('outerDiv').getAttribute(\"foo\"))\n\t\texpect(value).toBe(\"bar\")\n\t\tawait find('#div2').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('outerDiv').getAttribute(\"foo\"))).toBe(\"doh\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/closest",
    "name": "returns an array where appropriate",
    "html": "<div id='d2' class='bar'><div id='d1' class='foo' _='on click add .doh to closest .bar to .foo'></div></div><div id='d3' class='bar'><div class='foo'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d2' class='bar'><div id='d1' class='foo' _='on click add .doh to closest .bar to .foo'></div></div>\" +\n\t\t\t\"<div id='d3' class='bar'><div class='foo'></div></div>\"\n\t\t)\n\t\tlet hasDoh = await evaluate(() => document.getElementById('d2').classList.contains(\"doh\"))\n\t\texpect(hasDoh).toBe(false)\n\t\thasDoh = await evaluate(() => document.getElementById('d3').classList.contains(\"doh\"))\n\t\texpect(hasDoh).toBe(false)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d2').classList.contains(\"doh\"))).toBe(true)\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d3').classList.contains(\"doh\"))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "filters an array by condition",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}]\n\t\t\tthen return arr where its active`);\n\t\texpect(result.map(x => x.name)).toEqual([\"a\", \"c\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "filters with comparison",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [1, 2, 3, 4, 5]\n\t\t\tthen return arr where it > 3`);\n\t\texpect(result).toEqual([4, 5]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "full select-all pattern with multiple on features",
    "html": "\n\t\t\t<table>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\"></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input id=\"master\" type=\"checkbox\"\n\t\t\t\t_=\"set :checkboxes to <input[type=checkbox]/> in the closest <table/> where it is not me\n\t\t\t\t   on change\n\t\t\t\t     set checked of the :checkboxes to my checked\n\t\t\t\t   on change from the closest <table/>\n\t\t\t\t     if no :checkboxes where it is checked\n\t\t\t\t       set my indeterminate to false\n\t\t\t\t       set my checked to false\n\t\t\t\t     else if no :checkboxes where it is not checked\n\t\t\t\t       set my indeterminate to false\n\t\t\t\t       set my checked to true\n\t\t\t\t     else\n\t\t\t\t       set my indeterminate to true\n\t\t\t\t     end\"></td></tr>\n\t\t\t</table>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<table>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\"></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input id=\"master\" type=\"checkbox\"\n\t\t\t\t_=\"set :checkboxes to <input[type=checkbox]/> in the closest <table/> where it is not me\n\t\t\t\t   on change\n\t\t\t\t     set checked of the :checkboxes to my checked\n\t\t\t\t   on change from the closest <table/>\n\t\t\t\t     if no :checkboxes where it is checked\n\t\t\t\t       set my indeterminate to false\n\t\t\t\t       set my checked to false\n\t\t\t\t     else if no :checkboxes where it is not checked\n\t\t\t\t       set my indeterminate to false\n\t\t\t\t       set my checked to true\n\t\t\t\t     else\n\t\t\t\t       set my indeterminate to true\n\t\t\t\t     end\"></td></tr>\n\t\t\t</table>\n\t\t`);\n\t\t// master check should check all\n\t\tawait find('#master').click();\n\t\tawait expect.poll(() => find('.cb').first().isChecked()).toBe(true);\n\t\t// uncheck one child - master should become indeterminate\n\t\tawait find('.cb').first().click();\n\t\tvar indet = await evaluate(() => document.querySelector('#master').indeterminate);\n\t\texpect(indet).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "joined by on null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(`set x to null then return x joined by ','`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "mapped to on null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(`set x to null then return x mapped to (it * 2)`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "maps to a property",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"Alice\"}, {name: \"Bob\"}]\n\t\t\tthen return arr mapped to its name`);\n\t\texpect(result).toEqual([\"Alice\", \"Bob\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "maps with an expression",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [1, 2, 3]\n\t\t\tthen return arr mapped to (it * 2)`);\n\t\texpect(result).toEqual([2, 4, 6]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorted by binds after in without parens",
    "html": "<ul id='list'><li>C</li><li>A</li><li>B</li></ul>",
    "body": "\n\t\tawait html(\n\t\t\t\"<ul id='list'><li>C</li><li>A</li><li>B</li></ul>\"\n\t\t);\n\t\tvar result = await run(\"<li/> in #list where its textContent is not 'A'\");\n\t\texpect(result.length).toBe(2);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorted by on null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(`set x to null then return x sorted by it`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorted by then mapped to",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}]\n\t\t\tthen return arr sorted by its age mapped to its name`);\n\t\texpect(result).toEqual([\"Alice\", \"Charlie\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorts by a property",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}]\n\t\t\tthen return arr sorted by its name`);\n\t\texpect(result.map(x => x.name)).toEqual([\"Alice\", \"Bob\", \"Charlie\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorts descending",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [3, 1, 2]\n\t\t\tthen return arr sorted by it descending`);\n\t\texpect(result).toEqual([3, 2, 1]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "sorts numbers by a computed key",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}]\n\t\t\tthen return arr sorted by its age`);\n\t\texpect(result.map(x => x.name)).toEqual([\"a\", \"c\", \"b\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "split by on null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(`set x to null then return x split by ','\\n`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "the result inside where refers to previous command result, not current element",
    "html": "",
    "body": "\n\t\tvar result = await run(`get 3\n\t\t\tthen set arr to [1, 2, 3, 4, 5]\n\t\t\tthen return arr where it > the result`);\n\t\texpect(result).toEqual([4, 5]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where after in with mapped to",
    "html": "<ul id='items'><li class='yes'>A</li><li>B</li><li class='yes'>C</li></ul>",
    "body": "\n\t\tawait html(\n\t\t\t\"<ul id='items'><li class='yes'>A</li><li>B</li><li class='yes'>C</li></ul>\"\n\t\t);\n\t\tvar result = await run(\n\t\t\t\"<li/> in #items where it matches .yes mapped to its textContent\"\n\t\t);\n\t\texpect(result).toEqual([\"A\", \"C\"]);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where binds after in on closest",
    "html": "<div id='box'><span class='a'>A</span><span class='b'>B</span><span class='a'>C</span></div><button _=\"on click set result to (<span/> in #box) where it matches .a then put result.length into me\">go (parens)</button><button id='b2' _=\"on click set result to <span/> in #box where it matches .a then put result.length into me\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box'><span class='a'>A</span><span class='b'>B</span><span class='a'>C</span></div>\" +\n\t\t\t\"<button _=\\\"on click set result to (<span/> in #box) where it matches .a then put result.length into me\\\">go (parens)</button>\" +\n\t\t\t\"<button id='b2' _=\\\"on click set result to <span/> in #box where it matches .a then put result.length into me\\\">go</button>\"\n\t\t);\n\t\tawait find('button').first().click();\n\t\tawait expect(find('button').first()).toHaveText(\"2\");\n\t\tawait find('#b2').click();\n\t\tawait expect(find('#b2')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where binds after in without parens",
    "html": "<div id='container'><span class='a'>A</span><span class='b'>B</span><span class='a'>C</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='container'>\" +\n\t\t\t\"<span class='a'>A</span><span class='b'>B</span><span class='a'>C</span>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tvar result = await run(\"<span/> in #container where it matches .a\");\n\t\texpect(result.length).toBe(2);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where binds after property access",
    "html": "",
    "body": "\n\t\tvar result = await run(\"obj.items where it > 2\", {\n\t\t\tlocals: { obj: { items: [1, 2, 3, 4] } }\n\t\t});\n\t\texpect(result).toEqual([3, 4]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where in component init followed by on feature",
    "html": "\n\t\t\t<div id='box'><span class='a'>A</span><span class='b'>B</span></div>\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-where-comp\"\n\t\t\t\t_=\"set :items to <span/> in #box where it matches .a\n\t\t\t\t   on click put :items.length into me\">\n\t\t\t\t<slot/>\n\t\t\t</script>\n\t\t\t<test-where-comp>go</test-where-comp>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div id='box'><span class='a'>A</span><span class='b'>B</span></div>\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-where-comp\"\n\t\t\t\t_=\"set :items to <span/> in #box where it matches .a\n\t\t\t\t   on click put :items.length into me\">\n\t\t\t\t<slot/>\n\t\t\t</script>\n\t\t\t<test-where-comp>go</test-where-comp>\n\t\t`);\n\t\tawait find('test-where-comp').click();\n\t\tawait expect(find('test-where-comp')).toHaveText(\"1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where in init followed by on feature",
    "html": "<div id='box'><span class='a'>A</span><span class='b'>B</span></div><button _=\"set :items to <span/> in #box where it matches .a on click put :items.length into me\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box'><span class='a'>A</span><span class='b'>B</span></div>\" +\n\t\t\t\"<button _=\\\"set :items to <span/> in #box where it matches .a \" +\n\t\t\t\"on click put :items.length into me\\\">go</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tawait expect(find('button')).toHaveText(\"1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where on null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(`set x to null then return x where it > 1`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where on undefined returns undefined",
    "html": "",
    "body": "\n\t\tvar result = await run(`return doesNotExist where it > 1`);\n\t\texpect(result).toBeUndefined();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where then mapped to",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}]\n\t\t\tthen return arr where its active mapped to its name`);\n\t\texpect(result).toEqual([\"Alice\", \"Charlie\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where then sorted by then mapped to",
    "html": "",
    "body": "\n\t\tvar result = await run(`set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}]\n\t\t\tthen return arr where its active sorted by its age mapped to its name`);\n\t\texpect(result).toEqual([\"Bob\", \"Charlie\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where with is not me followed by on feature",
    "html": "\n\t\t\t<table>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\"></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input id=\"master\" type=\"checkbox\"\n\t\t\t\t_=\"set :checkboxes to <input[type=checkbox]/> in the closest <table/> where it is not me\n\t\t\t\t   on change set checked of the :checkboxes to my checked\"></td></tr>\n\t\t\t</table>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<table>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\"></td></tr>\n\t\t\t<tr><td><input type=\"checkbox\" class=\"cb\" checked></td></tr>\n\t\t\t<tr><td><input id=\"master\" type=\"checkbox\"\n\t\t\t\t_=\"set :checkboxes to <input[type=checkbox]/> in the closest <table/> where it is not me\n\t\t\t\t   on change set checked of the :checkboxes to my checked\"></td></tr>\n\t\t\t</table>\n\t\t`);\n\t\tawait find('#master').click();\n\t\tawait expect.poll(() => find('.cb').first().isChecked()).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "where with is not me in component template",
    "html": "\n\t\t\t<div id='box'><input type=\"checkbox\" class=\"cb\"><input type=\"checkbox\" class=\"cb\"></div>\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-where-me\">\n\t\t\t\t<input type=\"checkbox\" _=\"set :checkboxes to <input[type=checkbox]/> in #box where it is not me on change set checked of the :checkboxes to my checked\">\n\t\t\t</script>\n\t\t\t<test-where-me></test-where-me>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div id='box'><input type=\"checkbox\" class=\"cb\"><input type=\"checkbox\" class=\"cb\"></div>\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-where-me\">\n\t\t\t\t<input type=\"checkbox\" _=\"set :checkboxes to <input[type=checkbox]/> in #box where it is not me on change set checked of the :checkboxes to my checked\">\n\t\t\t</script>\n\t\t\t<test-where-me></test-where-me>\n\t\t`);\n\t\tvar attr = await evaluate(() => document.querySelector('test-where-me input')?.getAttribute('_'));\n\t\tconsole.log(\"ATTR:\", attr);\n\t\tawait find('test-where-me input').click();\n\t\tawait expect.poll(() => find('.cb').first().isChecked()).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/collectionExpressions",
    "name": "works with DOM elements",
    "html": "<ul id='list'><li class='yes'>A</li><li>B</li><li class='yes'>C</li></ul><button _='on click set items to <li/> in #list then set matches to items where it matches .yes then put matches mapped to its textContent into #out'>Go</button><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<ul id='list'><li class='yes'>A</li><li>B</li><li class='yes'>C</li></ul>\" +\n\t\t\t\"<button _='on click set items to <li/> in #list then set matches to items where it matches .yes then put matches mapped to its textContent into #out'>Go</button>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"AC\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "I am between works",
    "html": "",
    "body": "\n\t\texpect(await run(\"I am between 1 and 10\", { me: 5 })).toBe(true)\n\t\texpect(await run(\"I am between 1 and 10\", { me: 0 })).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "I am in works",
    "html": "",
    "body": "\n\t\texpect(await run(\"I am in [1, 2]\", { me: 1 })).toBe(true)\n\t\texpect(await run(\"I am in [1, 2]\", { me: 2 })).toBe(true)\n\t\texpect(await run(\"I am in [1, 2]\", { me: 3 })).toBe(false)\n\t\texpect(await run(\"I am in null\", { me: null })).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "I am not between works",
    "html": "",
    "body": "\n\t\texpect(await run(\"I am not between 1 and 10\", { me: 5 })).toBe(false)\n\t\texpect(await run(\"I am not between 1 and 10\", { me: 0 })).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "I am not in works",
    "html": "",
    "body": "\n\t\texpect(await run(\"I am not in [1, 2]\", { me: 1 })).toBe(false)\n\t\texpect(await run(\"I am not in [1, 2]\", { me: 2 })).toBe(false)\n\t\texpect(await run(\"I am not in [1, 2]\", { me: 3 })).toBe(true)\n\t\texpect(await run(\"I am not in null\", { me: null })).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "I precede works",
    "html": "<div id='a' _=\"on click if I precede #b put 'yes' into me\"></div><div id='b'></div>",
    "body": "\n\t\tawait html(\"<div id='a' _=\\\"on click if I precede #b put 'yes' into me\\\"></div><div id='b'></div>\")\n\t\tawait find('#a').dispatchEvent('click')\n\t\tawait expect(find('#a')).toHaveText(\"yes\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "am works as alias for is",
    "html": "",
    "body": "\n\t\texpect(await run(\"2 am 2\")).toBe(true)\n\t\texpect(await run(\"2 am 1\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "between works with strings",
    "html": "",
    "body": "\n\t\texpect(await run(\"'b' is between 'a' and 'c'\")).toBe(true)\n\t\texpect(await run(\"'d' is between 'a' and 'c'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "contains ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello World' contains 'hello' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' contains 'WORLD' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' contains 'missing' ignoring case\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "contains works with arrays",
    "html": "",
    "body": "\n\t\texpect(await run(\"I contain that\", { me: [1, 2, 3], locals: { that: 1 } })).toBe(true)\n\t\texpect(await run(\"that contains me\", { me: 1, locals: { that: [1, 2, 3] } })).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "contains works with css literals",
    "html": "<div id='d1' class='outer'><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\"<div id='d1' class='outer'><div id='d2'></div></div>\")\n\t\texpect(await run(\".outer contains #d2\")).toBe(true)\n\t\texpect(await run(\"#d2 contains #d1\")).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "contains works with elts",
    "html": "<div id='outer'><div id='inner'></div></div>",
    "body": "\n\t\tawait html(\"<div id='outer'><div id='inner'></div></div>\")\n\n\t\tlet result = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"I contain that\", { me: outer, locals: { that: inner } })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"I contain that\", { me: inner, locals: { that: outer } })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"that contains me\", { me: outer, locals: { that: inner } })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"that contains me\", { me: inner, locals: { that: outer } })\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not contain works",
    "html": "<div id='outer'><div id='inner'></div></div>",
    "body": "\n\t\tawait html(\"<div id='outer'><div id='inner'></div></div>\")\n\n\t\tlet result = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"I do not contain that\", { me: outer, locals: { that: inner } })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"I do not contain that\", { me: inner, locals: { that: outer } })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"that does not contains me\", { me: outer, locals: { that: inner } })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst outer = document.getElementById('outer')\n\t\t\tconst inner = document.getElementById('inner')\n\t\t\treturn _hyperscript(\"that does not contains me\", { me: inner, locals: { that: outer } })\n\t\t})\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not end with works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'hello world' does not end with 'world'\")).toBe(false)\n\t\texpect(await run(\"'hello world' does not end with 'hello'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not exist works",
    "html": "",
    "body": "\n\t\texpect(await run(\"undefined does not exist\")).toBe(true)\n\t\texpect(await run(\"null does not exist\")).toBe(true)\n\t\texpect(await run(\"#doesNotExist does not exist\")).toBe(true)\n\t\texpect(await run(\".aClassThatDoesNotExist does not exist\")).toBe(true)\n\t\texpect(await run(\"<.aClassThatDoesNotExist/> does not exist\")).toBe(true)\n\t\texpect(await run(\"<body/> does not exist\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not follow works",
    "html": "<div id='a'></div><div id='b'></div>",
    "body": "\n\t\tawait html(\"<div id='a'></div><div id='b'></div>\")\n\t\texpect(await evaluate(() => _hyperscript(\"x does not follow y\", {\n\t\t\tlocals: { x: document.getElementById('a'), y: document.getElementById('b') }\n\t\t}))).toBe(true)\n\t\texpect(await evaluate(() => _hyperscript(\"x does not follow y\", {\n\t\t\tlocals: { x: document.getElementById('b'), y: document.getElementById('a') }\n\t\t}))).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not match works",
    "html": "<div id='mDiv' class='foo'></div>",
    "body": "\n\t\tawait html(\"<div id='mDiv' class='foo'></div>\")\n\t\tlet result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"I do not match .foo\", { me: div })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"x does not match .foo\", { locals: { x: div } })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"I do not match .bar\", { me: div })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"x does not match .bar\", { locals: { x: div } })\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not match works w/ strings",
    "html": "",
    "body": "\n\t\texpect(await run(\"'a' does not match '.*'\")).toBe(false)\n\t\texpect(await run(\"'a' does not match 'b'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not precede works",
    "html": "<div id='a'></div><div id='b'></div>",
    "body": "\n\t\tawait html(\"<div id='a'></div><div id='b'></div>\")\n\t\texpect(await evaluate(() => _hyperscript(\"x does not precede y\", {\n\t\t\tlocals: { x: document.getElementById('b'), y: document.getElementById('a') }\n\t\t}))).toBe(true)\n\t\texpect(await evaluate(() => _hyperscript(\"x does not precede y\", {\n\t\t\tlocals: { x: document.getElementById('a'), y: document.getElementById('b') }\n\t\t}))).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "does not start with works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'hello world' does not start with 'hello'\")).toBe(false)\n\t\texpect(await run(\"'hello world' does not start with 'world'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "ends with coerces to string",
    "html": "",
    "body": "\n\t\texpect(await run(\"123 ends with '23'\")).toBe(true)\n\t\texpect(await run(\"123 ends with '12'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "ends with ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello World' ends with 'world' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' ends with 'WORLD' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' ends with 'hello' ignoring case\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "ends with null is false",
    "html": "",
    "body": "\n\t\texpect(await run(\"null ends with 'x'\")).toBe(false)\n\t\texpect(await run(\"null does not end with 'x'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "ends with works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'hello world' ends with 'world'\")).toBe(true)\n\t\texpect(await run(\"'hello world' ends with 'hello'\")).toBe(false)\n\t\texpect(await run(\"'hello' ends with 'hello'\")).toBe(true)\n\t\texpect(await run(\"'' ends with 'x'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "english greater than or equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is greater than or equal to 2\")).toBe(false)\n\t\texpect(await run(\"2 is greater than or equal to 1\")).toBe(true)\n\t\texpect(await run(\"2 is greater than or equal to 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "english greater than works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is greater than 2\")).toBe(false)\n\t\texpect(await run(\"2 is greater than 1\")).toBe(true)\n\t\texpect(await run(\"2 is greater than 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "english less than or equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is less than or equal to 2\")).toBe(true)\n\t\texpect(await run(\"2 is less than or equal to 1\")).toBe(false)\n\t\texpect(await run(\"2 is less than or equal to 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "english less than works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is less than 2\")).toBe(true)\n\t\texpect(await run(\"2 is less than 1\")).toBe(false)\n\t\texpect(await run(\"2 is less than 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 == 2\")).toBe(false)\n\t\texpect(await run(\"2 == 1\")).toBe(false)\n\t\texpect(await run(\"2 == 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "equals works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 equals 2\")).toBe(false)\n\t\texpect(await run(\"2 equals 1\")).toBe(false)\n\t\texpect(await run(\"2 equals 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "exists works",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\texpect(await run(\"undefined exists\")).toBe(false)\n\t\texpect(await run(\"null exists\")).toBe(false)\n\t\texpect(await run(\"#doesNotExist exists\")).toBe(false)\n\t\texpect(await run(\".aClassThatDoesNotExist exists\")).toBe(false)\n\t\texpect(await run(\"<.aClassThatDoesNotExist/> exists\")).toBe(false)\n\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\n\t\texpect(await run(\"#d1 exists\")).toBe(true)\n\t\texpect(await run(\".c1 exists\")).toBe(true)\n\t\texpect(await run(\"<body/> exists\")).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "follows works",
    "html": "<div id='a'></div><div id='b'></div><div id='c'></div>",
    "body": "\n\t\tawait html(\"<div id='a'></div><div id='b'></div><div id='c'></div>\")\n\t\texpect(await evaluate(() => _hyperscript(\"x follows y\", {\n\t\t\tlocals: { x: document.getElementById('b'), y: document.getElementById('a') }\n\t\t}))).toBe(true)\n\t\texpect(await evaluate(() => _hyperscript(\"x follows y\", {\n\t\t\tlocals: { x: document.getElementById('a'), y: document.getElementById('b') }\n\t\t}))).toBe(false)\n\t\texpect(await evaluate(() => _hyperscript(\"x follows y\", {\n\t\t\tlocals: { x: document.getElementById('c'), y: document.getElementById('a') }\n\t\t}))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "greater than or equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 >= 2\")).toBe(false)\n\t\texpect(await run(\"2 >= 1\")).toBe(true)\n\t\texpect(await run(\"2 >= 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "greater than works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 > 2\")).toBe(false)\n\t\texpect(await run(\"2 > 1\")).toBe(true)\n\t\texpect(await run(\"2 > 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "include works",
    "html": "",
    "body": "\n\t\texpect(await run(\"foo includes foobar\", { locals: { foo: \"foo\", foobar: \"foobar\" } })).toBe(false)\n\t\texpect(await run(\"foobar includes foo\", { locals: { foo: \"foo\", foobar: \"foobar\" } })).toBe(true)\n\t\texpect(await run(\"foo does not include foobar\", { locals: { foo: \"foo\", foobar: \"foobar\" } })).toBe(true)\n\t\texpect(await run(\"foobar does not include foo\", { locals: { foo: \"foo\", foobar: \"foobar\" } })).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "includes works with arrays",
    "html": "",
    "body": "\n\t\texpect(await run(\"I include that\", { me: [1, 2, 3], locals: { that: 1 } })).toBe(true)\n\t\texpect(await run(\"that includes me\", { me: 1, locals: { that: [1, 2, 3] } })).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "includes works with css literals",
    "html": "<div id='d1' class='outer'><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\"<div id='d1' class='outer'><div id='d2'></div></div>\")\n\t\texpect(await run(\".outer includes #d2\")).toBe(true)\n\t\texpect(await run(\"#d2 includes #d1\")).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is a Node works via instanceof",
    "html": "<div id='d1' _='on click if I am a Node put \"yes\" into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click if I am a Node put \\\"yes\\\" into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"yes\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is a works",
    "html": "",
    "body": "\n\t\texpect(await run(\"null is a String\")).toBe(true)\n\t\texpect(await run(\"null is a String!\")).toBe(false)\n\t\texpect(await run(\"'' is a String!\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is a works with instanceof fallback",
    "html": "<div id='d1' _='on click if I am a Element put \"yes\" into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click if I am a Element put \\\"yes\\\" into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"yes\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is an works",
    "html": "",
    "body": "\n\t\texpect(await run(\"null is an String\")).toBe(true)\n\t\texpect(await run(\"null is an String!\")).toBe(false)\n\t\texpect(await run(\"'' is an String!\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is between works",
    "html": "",
    "body": "\n\t\texpect(await run(\"5 is between 1 and 10\")).toBe(true)\n\t\texpect(await run(\"1 is between 1 and 10\")).toBe(true)\n\t\texpect(await run(\"10 is between 1 and 10\")).toBe(true)\n\t\texpect(await run(\"0 is between 1 and 10\")).toBe(false)\n\t\texpect(await run(\"11 is between 1 and 10\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is boolean property works in where clause",
    "html": "<input class='cb' type='checkbox' checked='checked'/><input class='cb' type='checkbox'/><input class='cb' type='checkbox' checked='checked'/>",
    "body": "\n\t\tawait html(\"<input class='cb' type='checkbox' checked='checked'/>\" +\n\t\t\t\"<input class='cb' type='checkbox'/>\" +\n\t\t\t\"<input class='cb' type='checkbox' checked='checked'/>\")\n\t\tvar result = await run(\".cb where it is checked\")\n\t\texpect(result.length).toBe(2)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is boolean property works with disabled",
    "html": "<button id='b1' disabled>Disabled</button><button id='b2'>Enabled</button>",
    "body": "\n\t\tawait html(\"<button id='b1' disabled>Disabled</button><button id='b2'>Enabled</button>\")\n\t\texpect(await run(\"#b1 is disabled\")).toBe(true)\n\t\texpect(await run(\"#b2 is disabled\")).toBe(false)\n\t\texpect(await run(\"#b2 is not disabled\")).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is empty works",
    "html": "",
    "body": "\n\t\texpect(await run(\"undefined is empty\")).toBe(true)\n\t\texpect(await run(\"'' is empty\")).toBe(true)\n\t\texpect(await run(\"[] is empty\")).toBe(true)\n\t\texpect(await run(\"'not empty' is empty\")).toBe(false)\n\t\texpect(await run(\"1000 is empty\")).toBe(false)\n\t\texpect(await run(\"[1,2,3] is empty\")).toBe(false)\n\t\texpect(await run(\".aClassThatDoesNotExist is empty\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is equal to works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is equal to 2\")).toBe(false)\n\t\texpect(await run(\"2 is equal to 1\")).toBe(false)\n\t\texpect(await run(\"2 is equal to 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is equal works without to",
    "html": "",
    "body": "\n\t\texpect(await run(\"2 is equal 2\")).toBe(true)\n\t\texpect(await run(\"2 is equal 1\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is falls back to boolean property when rhs is undefined",
    "html": "<input id='c1' type='checkbox' checked='checked'/><input id='c2' type='checkbox'/>",
    "body": "\n\t\tawait html(\"<input id='c1' type='checkbox' checked='checked'/><input id='c2' type='checkbox'/>\")\n\t\texpect(await run(\"#c1 is checked\")).toBe(true)\n\t\texpect(await run(\"#c2 is checked\")).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello' is 'hello' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello' is 'HELLO' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello' is 'world' ignoring case\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is in works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is in [1, 2]\")).toBe(true)\n\t\texpect(await run(\"2 is in [1, 2]\")).toBe(true)\n\t\texpect(await run(\"3 is in [1, 2]\")).toBe(false)\n\t\texpect(await run(\"3 is in null\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not a works",
    "html": "",
    "body": "\n\t\texpect(await run(\"null is not a String\")).toBe(false)\n\t\texpect(await run(\"null is not a String!\")).toBe(true)\n\t\texpect(await run(\"'' is not a String!\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not a works with instanceof fallback",
    "html": "<div id='d1' _='on click if \"hello\" is not a Element put \"yes\" into me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click if \\\"hello\\\" is not a Element put \\\"yes\\\" into me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"yes\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not an works",
    "html": "",
    "body": "\n\t\texpect(await run(\"null is not an String\")).toBe(false)\n\t\texpect(await run(\"null is not an String!\")).toBe(true)\n\t\texpect(await run(\"'' is not an String!\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not between works",
    "html": "",
    "body": "\n\t\texpect(await run(\"5 is not between 1 and 10\")).toBe(false)\n\t\texpect(await run(\"0 is not between 1 and 10\")).toBe(true)\n\t\texpect(await run(\"11 is not between 1 and 10\")).toBe(true)\n\t\texpect(await run(\"1 is not between 1 and 10\")).toBe(false)\n\t\texpect(await run(\"10 is not between 1 and 10\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not empty works",
    "html": "",
    "body": "\n\t\texpect(await run(\"undefined is not empty\")).toBe(false)\n\t\texpect(await run(\"'' is not empty\")).toBe(false)\n\t\texpect(await run(\"[] is not empty\")).toBe(false)\n\t\texpect(await run(\"'not empty' is not empty\")).toBe(true)\n\t\texpect(await run(\"1000 is not empty\")).toBe(true)\n\t\texpect(await run(\"[1,2,3] is not empty\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not equal to works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is not equal to 2\")).toBe(true)\n\t\texpect(await run(\"2 is not equal to 1\")).toBe(true)\n\t\texpect(await run(\"2 is not equal to 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not equal works without to",
    "html": "",
    "body": "\n\t\texpect(await run(\"2 is not equal 2\")).toBe(false)\n\t\texpect(await run(\"2 is not equal 1\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not falls back to boolean property when rhs is undefined",
    "html": "<input id='c1' type='checkbox' checked='checked'/><input id='c2' type='checkbox'/>",
    "body": "\n\t\tawait html(\"<input id='c1' type='checkbox' checked='checked'/><input id='c2' type='checkbox'/>\")\n\t\texpect(await run(\"#c1 is not checked\")).toBe(false)\n\t\texpect(await run(\"#c2 is not checked\")).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello' is not 'world' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello' is not 'hello' ignoring case\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not in works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is not in [1, 2]\")).toBe(false)\n\t\texpect(await run(\"2 is not in [1, 2]\")).toBe(false)\n\t\texpect(await run(\"3 is not in [1, 2]\")).toBe(true)\n\t\texpect(await run(\"3 is not in null\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not null still works as equality",
    "html": "",
    "body": "\n\t\texpect(await run(\"5 is not null\")).toBe(true)\n\t\texpect(await run(\"null is not null\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not really equal to works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is not really equal to 2\")).toBe(true)\n\t\texpect(await run(\"2 is not really equal to 1\")).toBe(true)\n\t\texpect(await run(\"2 is not really equal to '2'\")).toBe(true)\n\t\texpect(await run(\"2 is not really equal to 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not really works without equal to",
    "html": "",
    "body": "\n\t\texpect(await run(\"2 is not really '2'\")).toBe(true)\n\t\texpect(await run(\"2 is not really 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not undefined still works as equality",
    "html": "",
    "body": "\n\t\texpect(await run(\"5 is not undefined\")).toBe(true)\n\t\texpect(await run(\"null is not undefined\")).toBe(false)  // null == undefined in JS\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is not works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is not 2\")).toBe(true)\n\t\texpect(await run(\"2 is not 1\")).toBe(true)\n\t\texpect(await run(\"2 is not 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is really equal to works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is really equal to 2\")).toBe(false)\n\t\texpect(await run(\"2 is really equal to 1\")).toBe(false)\n\t\texpect(await run(\"2 is really equal to '2'\")).toBe(false)\n\t\texpect(await run(\"2 is really equal to 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is really works without equal to",
    "html": "",
    "body": "\n\t\texpect(await run(\"2 is really 2\")).toBe(true)\n\t\texpect(await run(\"2 is really '2'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is still does equality when rhs variable exists",
    "html": "",
    "body": "\n\t\texpect(await run(\"x is y\", { locals: { x: 5, y: 5 } })).toBe(true)\n\t\texpect(await run(\"x is y\", { locals: { x: 5, y: 6 } })).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "is works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 is 2\")).toBe(false)\n\t\texpect(await run(\"2 is 1\")).toBe(false)\n\t\texpect(await run(\"2 is 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "less than or equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 <= 2\")).toBe(true)\n\t\texpect(await run(\"2 <= 1\")).toBe(false)\n\t\texpect(await run(\"2 <= 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "less than works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 < 2\")).toBe(true)\n\t\texpect(await run(\"2 < 1\")).toBe(false)\n\t\texpect(await run(\"2 < 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "match works",
    "html": "<div id='mDiv' class='foo'></div>",
    "body": "\n\t\tawait html(\"<div id='mDiv' class='foo'></div>\")\n\t\tlet result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"I match .foo\", { me: div })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"x matches .foo\", { locals: { x: div } })\n\t\t})\n\t\texpect(result).toBe(true)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"I match .bar\", { me: div })\n\t\t})\n\t\texpect(result).toBe(false)\n\n\t\tresult = await evaluate(() => {\n\t\t\tconst div = document.getElementById('mDiv')\n\t\t\treturn _hyperscript(\"x matches .bar\", { locals: { x: div } })\n\t\t})\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "match works w/ strings",
    "html": "",
    "body": "\n\t\texpect(await run(\"'a' matches '.*'\")).toBe(true)\n\t\texpect(await run(\"'a' matches 'b'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "matches ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello' matches 'hello' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello' matches 'HELLO' ignoring case\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "not equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 != 2\")).toBe(true)\n\t\texpect(await run(\"2 != 1\")).toBe(true)\n\t\texpect(await run(\"2 != 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "precedes with null is false",
    "html": "",
    "body": "\n\t\texpect(await run(\"null precedes null\")).toBe(false)\n\t\texpect(await run(\"null does not precede null\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "precedes works",
    "html": "<div id='a'></div><div id='b'></div><div id='c'></div>",
    "body": "\n\t\tawait html(\"<div id='a'></div><div id='b'></div><div id='c'></div>\")\n\t\texpect(await evaluate(() => _hyperscript(\"x precedes y\", {\n\t\t\tlocals: { x: document.getElementById('a'), y: document.getElementById('b') }\n\t\t}))).toBe(true)\n\t\texpect(await evaluate(() => _hyperscript(\"x precedes y\", {\n\t\t\tlocals: { x: document.getElementById('b'), y: document.getElementById('a') }\n\t\t}))).toBe(false)\n\t\texpect(await evaluate(() => _hyperscript(\"x precedes y\", {\n\t\t\tlocals: { x: document.getElementById('a'), y: document.getElementById('c') }\n\t\t}))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "really equals works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 really equals 2\")).toBe(false)\n\t\texpect(await run(\"2 really equals 1\")).toBe(false)\n\t\texpect(await run(\"2 really equals 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "starts with coerces to string",
    "html": "",
    "body": "\n\t\texpect(await run(\"123 starts with '12'\")).toBe(true)\n\t\texpect(await run(\"123 starts with '23'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "starts with ignoring case works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'Hello World' starts with 'hello' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' starts with 'HELLO' ignoring case\")).toBe(true)\n\t\texpect(await run(\"'Hello World' starts with 'world' ignoring case\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "starts with null is false",
    "html": "",
    "body": "\n\t\texpect(await run(\"null starts with 'x'\")).toBe(false)\n\t\texpect(await run(\"null does not start with 'x'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "starts with works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'hello world' starts with 'hello'\")).toBe(true)\n\t\texpect(await run(\"'hello world' starts with 'world'\")).toBe(false)\n\t\texpect(await run(\"'hello' starts with 'hello'\")).toBe(true)\n\t\texpect(await run(\"'' starts with 'x'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "triple equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 === 2\")).toBe(false)\n\t\texpect(await run(\"2 === 1\")).toBe(false)\n\t\texpect(await run(\"2 === 2\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/comparisonOperator",
    "name": "triple not equal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 !== 2\")).toBe(true)\n\t\texpect(await run(\"2 !== 1\")).toBe(true)\n\t\texpect(await run(\"2 !== 2\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/cookies",
    "name": "basic clear cookie values work",
    "html": "",
    "body": "\n\t\tconst result = await page.evaluate(() => {\n\t\t\t_hyperscript(\"set cookies.foo to 'bar'\")\n\t\t\t_hyperscript(\"cookies.clear('foo')\")\n\t\t\treturn _hyperscript(\"cookies.foo\")\n\t\t})\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/cookies",
    "name": "basic set cookie values work",
    "html": "",
    "body": "\n\t\tconst result = await page.evaluate(() => {\n\t\t\tconst r1 = _hyperscript(\"cookies.foo\")\n\t\t\t_hyperscript(\"set cookies.foo to 'bar'\")\n\t\t\tconst r2 = _hyperscript(\"cookies.foo\")\n\t\t\treturn { before: r1, after: r2 }\n\t\t})\n\t\texpect(result.before).toBeUndefined()\n\t\texpect(result.after).toBe('bar')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/cookies",
    "name": "iterate cookies values work",
    "html": "",
    "body": "\n\t\tconst result = await page.evaluate(() => {\n\t\t\t_hyperscript(\"set cookies.foo to 'bar'\")\n\t\t\tconst context = { me: [], you: [] }\n\t\t\t_hyperscript(\"for x in cookies me.push(x.name) then you.push(x.value) end\", context)\n\t\t\treturn { names: context.me, values: context.you }\n\t\t})\n\t\texpect(result.names).toContain('foo')\n\t\texpect(result.values).toContain('bar')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/cookies",
    "name": "length is 0 when no cookies are set",
    "html": "",
    "body": "\n\t\tconst result = await page.evaluate(() => {\n\t\t\t// Clear all cookies first\n\t\t\tfor (var c of document.cookie.split(\"; \")) {\n\t\t\t\tvar name = c.split(\"=\")[0]\n\t\t\t\tif (name) document.cookie = name + \"=;expires=Thu, 01 Jan 1970 00:00:00 GMT\"\n\t\t\t}\n\t\t\treturn _hyperscript(\"cookies.length\")\n\t\t})\n\t\texpect(result).toBe(0)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/cookies",
    "name": "update cookie values work",
    "html": "",
    "body": "\n\t\tconst result = await page.evaluate(() => {\n\t\t\t_hyperscript(\"set cookies.foo to 'bar'\")\n\t\t\tconst r1 = _hyperscript(\"cookies.foo\")\n\t\t\t_hyperscript(\"set cookies.foo to 'doh'\")\n\t\t\tconst r2 = _hyperscript(\"cookies.foo\")\n\t\t\treturn { first: r1, second: r2 }\n\t\t})\n\t\texpect(result.first).toBe('bar')\n\t\texpect(result.second).toBe('doh')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/dom-scope",
    "name": "always reacts to ^var changes",
    "html": "<div _=\"init set ^name to 'alice'\">  <button _=\"on click set ^name to 'bob'\">rename</button>  <output _=\"live put 'Hello ' + ^name into me\">loading</output></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^name to 'alice'\">` +\n\t\t\t`  <button _=\"on click set ^name to 'bob'\">rename</button>` +\n\t\t\t`  <output _=\"live put 'Hello ' + ^name into me\">loading</output>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect.poll(() => find('output').textContent()).toBe('Hello alice')\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('output').textContent()).toBe('Hello bob')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "bind works with ^var",
    "html": "<div _=\"init set ^search to ''\">  <input type=\"text\" _=\"bind ^search and my value\" />  <output _=\"when ^search changes put it into me\"></output></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^search to ''\">` +\n\t\t\t`  <input type=\"text\" _=\"bind ^search and my value\" />` +\n\t\t\t`  <output _=\"when ^search changes put it into me\"></output>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('input').fill('hello')\n\t\tawait expect.poll(() => find('output').textContent()).toBe('hello')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "child reads ^var set by parent",
    "html": "<div _=\"init set ^count to 42\">  <span _=\"on click put ^count into me\">0</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^count to 42\">` +\n\t\t\t`  <span _=\"on click put ^count into me\">0</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "child write updates the ancestor, not a local copy",
    "html": "<div _=\"init set ^shared to 0\">  <button _=\"on click set ^shared to 10\">set</button>  <span _=\"on click put ^shared into me\">0</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^shared to 0\">` +\n\t\t\t`  <button _=\"on click set ^shared to 10\">set</button>` +\n\t\t\t`  <span _=\"on click put ^shared into me\">0</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('button').click()\n\t\t// read from a sibling - should see the ancestor's updated value\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('10')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "child writes ^var and parent sees it",
    "html": "<div _=\"init set ^count to 0\">  <button _=\"on click set ^count to 99\">set</button>  <span _=\"on click put ^count into me\">0</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^count to 0\">` +\n\t\t\t`  <button _=\"on click set ^count to 99\">set</button>` +\n\t\t\t`  <span _=\"on click put ^count into me\">0</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('button').click()\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('99')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "closest ancestor wins (shadowing)",
    "html": "<div _=\"init set ^color to 'red'\">  <div _=\"init set ^color to 'blue'\">    <span _=\"on click put ^color into me\">empty</span>  </div></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^color to 'red'\">` +\n\t\t\t`  <div _=\"init set ^color to 'blue'\">` +\n\t\t\t`    <span _=\"on click put ^color into me\">empty</span>` +\n\t\t\t`  </div>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('blue')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "dedup prevents re-fire on same ^var value",
    "html": "<div _=\"init set ^val to 'same'\">  <output _=\"when ^val changes increment :runs then put :runs into me\"></output>  <button _=\"on click set ^val to 'same'\">same</button>  <button id=\"diff\" _=\"on click set ^val to 'different'\">diff</button></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^val to 'same'\">` +\n\t\t\t`  <output _=\"when ^val changes increment :runs then put :runs into me\"></output>` +\n\t\t\t`  <button _=\"on click set ^val to 'same'\">same</button>` +\n\t\t\t`  <button id=\"diff\" _=\"on click set ^val to 'different'\">diff</button>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect(find('output')).toHaveText('1')\n\t\tawait find('button:text(\"same\")').click()\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('output')).toHaveText('1')\n\t\tawait find('#diff').click()\n\t\tawait expect(find('output')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/dom-scope",
    "name": "deeply nested child reads ^var from grandparent",
    "html": "<div _=\"init set ^name to 'alice'\">  <div>    <div>      <span _=\"on click put ^name into me\">empty</span>    </div>  </div></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^name to 'alice'\">` +\n\t\t\t`  <div>` +\n\t\t\t`    <div>` +\n\t\t\t`      <span _=\"on click put ^name into me\">empty</span>` +\n\t\t\t`    </div>` +\n\t\t\t`  </div>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('alice')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "derived ^var chains reactively",
    "html": "<div _=\"init set ^price to 10 then set ^qty to 2 then set ^total to 20\">  <span _=\"when ^price changes set ^total to (^price * ^qty)\"></span>  <span _=\"when ^qty changes set ^total to (^price * ^qty)\"></span>  <output _=\"when ^total changes put it into me\"></output>  <button id=\"price-btn\" _=\"on click set ^price to 25\">set price</button>  <button id=\"qty-btn\" _=\"on click set ^qty to 5\">set qty</button></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^price to 10 then set ^qty to 2 then set ^total to 20\">` +\n\t\t\t`  <span _=\"when ^price changes set ^total to (^price * ^qty)\"></span>` +\n\t\t\t`  <span _=\"when ^qty changes set ^total to (^price * ^qty)\"></span>` +\n\t\t\t`  <output _=\"when ^total changes put it into me\"></output>` +\n\t\t\t`  <button id=\"price-btn\" _=\"on click set ^price to 25\">set price</button>` +\n\t\t\t`  <button id=\"qty-btn\" _=\"on click set ^qty to 5\">set qty</button>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect.poll(() => find('output').textContent()).toBe('20')\n\t\tawait find('#price-btn').click()\n\t\tawait expect.poll(() => find('output').textContent()).toBe('50')\n\t\tawait find('#qty-btn').click()\n\t\tawait expect.poll(() => find('output').textContent()).toBe('125')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "dom keyword works as scope modifier",
    "html": "<div _=\"init set dom count to 42\">  <span _=\"on click put dom count into me\">0</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set dom count to 42\">` +\n\t\t\t`  <span _=\"on click put dom count into me\">0</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "effect stops when element is removed",
    "html": "<div _=\"init set ^msg to 'hello'\">  <output _=\"when ^msg changes put it into me\"></output>  <button _=\"on click set ^msg to 'updated'\">update</button></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^msg to 'hello'\">` +\n\t\t\t`  <output _=\"when ^msg changes put it into me\"></output>` +\n\t\t\t`  <button _=\"on click set ^msg to 'updated'\">update</button>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect(find('output')).toHaveText('hello')\n\t\tawait evaluate(() => document.querySelector('#work-area output').remove())\n\t\tawait find('button').click()\n\t\t// Output was removed - no error, effect is disposed\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/dom-scope",
    "name": "increment works on inherited var",
    "html": "<div _=\"init set ^count to 0\">  <button _=\"on click increment ^count\">+1</button>  <span _=\"on click put ^count into me\">0</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^count to 0\">` +\n\t\t\t`  <button _=\"on click increment ^count\">+1</button>` +\n\t\t\t`  <span _=\"on click put ^count into me\">0</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('button').click()\n\t\tawait find('button').click()\n\t\tawait find('button').click()\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('3')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "multiple children react to same ^var",
    "html": "<div _=\"init set ^color to 'red'\">  <button _=\"on click set ^color to 'blue'\">change</button>  <span id=\"a\" _=\"when ^color changes put it into me\"></span>  <span id=\"b\" _=\"when ^color changes put it into me\"></span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^color to 'red'\">` +\n\t\t\t`  <button _=\"on click set ^color to 'blue'\">change</button>` +\n\t\t\t`  <span id=\"a\" _=\"when ^color changes put it into me\"></span>` +\n\t\t\t`  <span id=\"b\" _=\"when ^color changes put it into me\"></span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect(find('#a')).toHaveText('red')\n\t\tawait expect(find('#b')).toHaveText('red')\n\t\tawait find('button').click()\n\t\tawait expect(find('#a')).toHaveText('blue')\n\t\tawait expect(find('#b')).toHaveText('blue')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "on clause targets a specific ancestor",
    "html": "<div class=\"outer\" _=\"init set ^outerVal to 'outer'\">  <div class=\"inner\" _=\"init set ^innerVal to 'inner'\">    <span _=\"on click put ^outerVal on closest .outer into me\">read</span>  </div></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div class=\"outer\" _=\"init set ^outerVal to 'outer'\">` +\n\t\t\t`  <div class=\"inner\" _=\"init set ^innerVal to 'inner'\">` +\n\t\t\t`    <span _=\"on click put ^outerVal on closest .outer into me\">read</span>` +\n\t\t\t`  </div>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('outer')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "on clause with id reference",
    "html": "<div id=\"state-holder\"></div><button _=\"on click set ^count on #state-holder to 99\">set</button><span _=\"on click put ^count on #state-holder into me\">read</span>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"state-holder\"></div>` +\n\t\t\t`<button _=\"on click set ^count on #state-holder to 99\">set</button>` +\n\t\t\t`<span _=\"on click put ^count on #state-holder into me\">read</span>`\n\t\t)\n\t\tawait find('button').click()\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('99')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "set ^var on explicit element",
    "html": "<div class=\"store\">  <button _=\"on click set ^data on closest .store to 'hello'\">set</button>  <span _=\"on click put ^data on closest .store into me\">read</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div class=\"store\">` +\n\t\t\t`  <button _=\"on click set ^data on closest .store to 'hello'\">set</button>` +\n\t\t\t`  <span _=\"on click put ^data on closest .store into me\">read</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('button').click()\n\t\tawait find('span').click()\n\t\tawait expect(find('span')).toHaveText('hello')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "sibling subtrees have independent ^vars",
    "html": "<div id=\"a\" _=\"init set ^val to 'A'\">  <span _=\"on click put ^val into me\">empty</span></div><div id=\"b\" _=\"init set ^val to 'B'\">  <span _=\"on click put ^val into me\">empty</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"a\" _=\"init set ^val to 'A'\">` +\n\t\t\t`  <span _=\"on click put ^val into me\">empty</span>` +\n\t\t\t`</div>` +\n\t\t\t`<div id=\"b\" _=\"init set ^val to 'B'\">` +\n\t\t\t`  <span _=\"on click put ^val into me\">empty</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('#a span').click()\n\t\tawait expect(find('#a span')).toHaveText('A')\n\t\tawait find('#b span').click()\n\t\tawait expect(find('#b span')).toHaveText('B')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "sibling subtrees react independently with ^var",
    "html": "<div id=\"a\" _=\"init set ^val to 0\">  <button _=\"on click increment ^val\">+1</button>  <output _=\"when ^val changes put it into me\">0</output></div><div id=\"b\" _=\"init set ^val to 0\">  <button _=\"on click increment ^val\">+1</button>  <output _=\"when ^val changes put it into me\">0</output></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"a\" _=\"init set ^val to 0\">` +\n\t\t\t`  <button _=\"on click increment ^val\">+1</button>` +\n\t\t\t`  <output _=\"when ^val changes put it into me\">0</output>` +\n\t\t\t`</div>` +\n\t\t\t`<div id=\"b\" _=\"init set ^val to 0\">` +\n\t\t\t`  <button _=\"on click increment ^val\">+1</button>` +\n\t\t\t`  <output _=\"when ^val changes put it into me\">0</output>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect(find('#a output')).toHaveText('0')\n\t\tawait expect(find('#b output')).toHaveText('0')\n\n\t\tawait find('#a button').click()\n\t\tawait find('#a button').click()\n\t\tawait expect(find('#a output')).toHaveText('2')\n\t\tawait expect(find('#b output')).toHaveText('0')\n\n\t\tawait find('#b button').click()\n\t\tawait expect(find('#b output')).toHaveText('1')\n\t\tawait expect(find('#a output')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "when reacts to ^var changes",
    "html": "<div _=\"init set ^count to 0\">  <button _=\"on click increment ^count\">+1</button>  <output _=\"when ^count changes put it into me\">0</output></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set ^count to 0\">` +\n\t\t\t`  <button _=\"on click increment ^count\">+1</button>` +\n\t\t\t`  <output _=\"when ^count changes put it into me\">0</output>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait expect(find('output')).toHaveText('0')\n\t\tawait find('button').click()\n\t\tawait expect(find('output')).toHaveText('1')\n\t\tawait find('button').click()\n\t\tawait expect(find('output')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/dom-scope",
    "name": "write to ^var not found anywhere creates on current element",
    "html": "<div>  <button _=\"on click set ^newvar to 'created' then put ^newvar into next <span/>\">go</button>  <span>empty</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div>` +\n\t\t\t`  <button _=\"on click set ^newvar to 'created' then put ^newvar into next <span/>\">go</button>` +\n\t\t\t`  <span>empty</span>` +\n\t\t\t`</div>`\n\t\t)\n\t\tawait find('button').click()\n\t\tawait expect(find('span')).toHaveText('created')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can access a property of a call's result",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.makePoint = (x, y) => ({ x: x, y: y }) })\n\t\texpect(await run(\"makePoint(3, 4).x\")).toBe(3)\n\t\texpect(await run(\"makePoint(3, 4).y\")).toBe(4)\n\t\tawait evaluate(() => { delete window.makePoint })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can chain calls on the result of a call",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.getObj = () => ({ greet: () => 'hi' })\n\t\t})\n\t\texpect(await run(\"getObj().greet()\")).toBe('hi')\n\t\tawait evaluate(() => { delete window.getObj })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can invoke function on object",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.obj = {\n\t\t\t\tvalue: \"foo\",\n\t\t\t\tgetValue: function () { return this.value },\n\t\t\t}\n\t\t})\n\t\tconst result = await run(\"obj.getValue()\")\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can invoke function on object w/ async arg",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.obj = { identity: (x) => x }\n\t\t})\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"obj.identity(promiseAnIntIn(10))\").then(r => r)\n\t\t})\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can invoke function on object w/ async root & arg",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.obj = { identity: (x) => x }\n\t\t})\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"promiseValueBackIn(obj, 20).identity(promiseAnIntIn(10))\").then(r => r)\n\t\t})\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can invoke global function",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.identity = (x) => x })\n\t\tconst result = await run('identity(\"foo\")')\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can invoke global function w/ async arg",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.identity = (x) => x })\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"identity(promiseAnIntIn(10))\").then(r => r)\n\t\t})\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can pass an array literal as an argument",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.sum = (arr) => arr.reduce((a, b) => a + b, 0) })\n\t\texpect(await run(\"sum([1, 2, 3, 4])\")).toBe(10)\n\t\tawait evaluate(() => { delete window.sum })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can pass an expression as an argument",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.double = (n) => n * 2 })\n\t\texpect(await run(\"double(3 + 4)\")).toBe(14)\n\t\tawait evaluate(() => { delete window.double })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can pass an object literal as an argument",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.getName = (o) => o.name })\n\t\texpect(await run(\"getName({name: 'Alice'})\")).toBe('Alice')\n\t\tawait evaluate(() => { delete window.getName })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can pass multiple arguments",
    "html": "<div _=\"on click put add(1, 2, 3) into me\"></div>",
    "body": "\n\t\tawait evaluate(() => { window.add = (a, b, c) => a + b + c })\n\t\tawait html(`<div _=\"on click put add(1, 2, 3) into me\"></div>`)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('6')\n\t\tawait evaluate(() => { delete window.add })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/functionCalls",
    "name": "can pass no arguments",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.getFortyTwo = () => 42 })\n\t\texpect(await run(\"getFortyTwo()\")).toBe(42)\n\t\tawait evaluate(() => { delete window.getFortyTwo })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/idRef",
    "name": "basic id ref works",
    "html": "<div id='d1'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('d1')\n\t\t\treturn _hyperscript(\"#d1\") === div\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/idRef",
    "name": "basic id ref works w no match",
    "html": "<div></div>",
    "body": "\n\t\tawait html(\"<div></div>\")\n\t\tconst result = await run(\"#d1\")\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/idRef",
    "name": "id ref works from a disconnected element",
    "html": "<div id='d1'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('d1')\n\t\t\treturn _hyperscript(\"#d1\", { me: document.createElement('div') }) === div\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/idRef",
    "name": "template id ref works",
    "html": "<div id='d1'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('d1')\n\t\t\treturn _hyperscript(\"#{'d1'}\") === div\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "basic no query return values",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 in [1, 2, 3]\")).toEqual([1])\n\t\texpect(await run(\"[1, 3] in [1, 2, 3]\")).toEqual([1, 3])\n\t\texpect(await run(\"[1, 3, 4] in [1, 2, 3]\")).toEqual([1, 3])\n\t\texpect(await run(\"[4, 5, 6] in [1, 2, 3]\")).toEqual([])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/in",
    "name": "basic query return values",
    "html": "<div id='d1'><p></p><p></p></div>",
    "body": "\n\t\tawait html(\"<div id='d1'><p></p><p></p></div>\")\n\t\tlet result = await evaluate(() => _hyperscript(\"<p/> in #d1\").length)\n\t\texpect(result).toBe(2)\n\n\t\tawait html(\"<div id='d2'><p class='foo'></p><p></p></div>\")\n\t\tresult = await evaluate(() => _hyperscript(\"<p.foo/> in #d2\").length)\n\t\texpect(result).toBe(1)\n\n\t\tawait html(\"<div id='d3'><p class='foo'></p><p></p></div>\")\n\t\tresult = await evaluate(() => _hyperscript(\"<p.foo/> in <div#d3/>\").length)\n\t\texpect(result).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "class returns values",
    "html": "<div id='inDiv' _='on click get the first .p1 in me then put its id into @result'><p id='p1' class='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get the first .p1 in me then put its id into @result'><p id='p1' class='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "class template returns values",
    "html": "<div id='inDiv' _='on click get the first .{\"p1\"} in me then put its id into @result'><p id='p1' class='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get the first .{\\\"p1\\\"} in me then put its id into @result'><p id='p1' class='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "id returns values",
    "html": "<div id='inDiv' _='on click get #p1 in me then put its id into @result'><p id='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get #p1 in me then put its id into @result'><p id='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "id template returns values",
    "html": "<div id='inDiv' _='on click get #{\"p1\"} in me then put its id into @result'><p id='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get #{\\\"p1\\\"} in me then put its id into @result'><p id='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "in expression binds to unaryOperators",
    "html": "<div id='d2'><p class='foo'>bar</p><p></p></div>",
    "body": "\n\t\tawait html(\"<div id='d2'><p class='foo'>bar</p><p></p></div>\")\n\t\tconst result = await run(\"the innerText of the first <p.foo/> in #d2 is 'bar'\")\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "null value in array returns empty",
    "html": "",
    "body": "\n\t\texpect(await run(\"null in [1, 2, 3]\")).toEqual([])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/in",
    "name": "query returns values",
    "html": "<div id='inDiv' _='on click get the first <p/> in me then put its id into @result'><p id='p1' class='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get the first <p/> in me then put its id into @result'><p id='p1' class='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/in",
    "name": "query template returns values",
    "html": "<div id='inDiv' _='on click get the first <${\"p\"}/> in me then put its id into @result'><p id='p1' class='p1'></p></div>",
    "body": "\n\t\tawait html(\"<div id='inDiv' _='on click get the first <${\\\"p\\\"}/> in me then put its id into @result'><p id='p1' class='p1'></p></div>\")\n\t\tawait find('#inDiv').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('inDiv').getAttribute(\"result\"))).toBe('p1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "and short-circuits when lhs promise resolves to false",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(async () => {\n\t\t\tlet rhsCalled = false\n\t\t\tconst returnsFalse = () => Promise.resolve(false)\n\t\t\tconst rhs = () => { rhsCalled = true; return true }\n\t\t\tconst r = await _hyperscript(\"returnsFalse() and rhs()\", {locals: {returnsFalse, rhs}})\n\t\t\treturn { result: r, rhsCalled }\n\t\t})\n\t\texpect(result.result).toBe(false)\n\t\texpect(result.rhsCalled).toBe(false)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "and works",
    "html": "",
    "body": "\n\t\tconst result = await run(\"true and false\")\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "and works w/ more than one value",
    "html": "",
    "body": "\n\t\tconst result = await run(\"true and true and false\")\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "or evaluates rhs when lhs promise resolves to false",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(async () => {\n\t\t\tlet rhsCalled = false\n\t\t\tconst returnsFalse = () => Promise.resolve(false)\n\t\t\tconst rhs = () => { rhsCalled = true; return \"fallback\" }\n\t\t\tconst r = await _hyperscript(\"returnsFalse() or rhs()\", {locals: {returnsFalse, rhs}})\n\t\t\treturn { result: r, rhsCalled }\n\t\t})\n\t\texpect(result.result).toBe(\"fallback\")\n\t\texpect(result.rhsCalled).toBe(true)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "or short-circuits when lhs promise resolves to true",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(async () => {\n\t\t\tlet rhsCalled = false\n\t\t\tconst returnsTrue = () => Promise.resolve(true)\n\t\t\tconst rhs = () => { rhsCalled = true; return false }\n\t\t\tconst r = await _hyperscript(\"returnsTrue() or rhs()\", {locals: {returnsTrue, rhs}})\n\t\t\treturn { result: r, rhsCalled }\n\t\t})\n\t\texpect(result.result).toBe(true)\n\t\texpect(result.rhsCalled).toBe(false)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "or works",
    "html": "",
    "body": "\n\t\tconst result = await run(\"true or false\")\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "parenthesized expressions with multiple operators work",
    "html": "",
    "body": "\n\t\tconst result = await run(\"true and (false or true)\")\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "should short circuit with and expression",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tlet func1Called = false\n\t\t\tlet func2Called = false\n\t\t\tconst func1 = () => { func1Called = true; return false }\n\t\t\tconst func2 = () => { func2Called = true; return false }\n\t\t\tconst r = _hyperscript(\"func1() and func2()\", {locals: {func1, func2}})\n\t\t\treturn { result: r, func1Called, func2Called }\n\t\t})\n\t\texpect(result.result).toBe(false)\n\t\texpect(result.func1Called).toBe(true)\n\t\texpect(result.func2Called).toBe(false)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "should short circuit with or expression",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tlet func1Called = false\n\t\t\tlet func2Called = false\n\t\t\tconst func1 = () => { func1Called = true; return true }\n\t\t\tconst func2 = () => { func2Called = true; return true }\n\t\t\tconst r = _hyperscript(\"func1() or func2()\", {locals: {func1, func2}})\n\t\t\treturn { result: r, func1Called, func2Called }\n\t\t})\n\t\texpect(result.result).toBe(true)\n\t\texpect(result.func1Called).toBe(true)\n\t\texpect(result.func2Called).toBe(false)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/logicalOperator",
    "name": "unparenthesized expressions with multiple operators cause an error",
    "html": "",
    "body": "\n\t\tconst msg = await error(\"true and false or true\")\n\t\texpect(msg).toMatch(/^You must parenthesize logical operations with different operators/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/mathOperator",
    "name": "addition works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 + 1\")).toBe(2)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "addition works w/ more than one value",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 + 2 + 3\")).toBe(6)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "array + array concats",
    "html": "",
    "body": "\n\t\texpect(await run(\"[1, 2] + [3, 4]\")).toEqual([1, 2, 3, 4])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "array + array does not mutate original",
    "html": "",
    "body": "\n\t\tvar result = await run(\"set a to [1, 2] then set b to a + [3] then return a\")\n\t\texpect(result).toEqual([1, 2])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "array + single value appends",
    "html": "",
    "body": "\n\t\texpect(await run(\"[1, 2] + 3\")).toEqual([1, 2, 3])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "array concat chains",
    "html": "",
    "body": "\n\t\texpect(await run(\"[1] + [2] + [3]\")).toEqual([1, 2, 3])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "can use mixed expressions",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"1 + promiseAnIntIn(10)\").then(v => v)\n\t\t})\n\t\texpect(result).toBe(43)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/mathOperator",
    "name": "division works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 / 2\")).toBe(0.5)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "empty array + array works",
    "html": "",
    "body": "\n\t\texpect(await run(\"[] + [1, 2]\")).toEqual([1, 2])\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "mod works",
    "html": "",
    "body": "\n\t\texpect(await run(\"3 mod 2\")).toBe(1)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "multiplication works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 * 2\")).toBe(2)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "parenthesized expressions with multiple operators work",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 + (2 * 3)\")).toBe(7)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "string concat works",
    "html": "",
    "body": "\n\t\texpect(await run(\"'a' + 'b'\")).toBe(\"ab\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "subtraction works",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 - 1\")).toBe(0)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/mathOperator",
    "name": "unparenthesized expressions with multiple operators cause an error",
    "html": "",
    "body": "\n\t\tconst msg = await error(\"1 + 2 * 3\")\n\t\texpect(msg).toMatch(/^You must parenthesize math operations with different operators/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/no",
    "name": "no returns false for non-empty array",
    "html": "",
    "body": "\n\t\texpect(await run(\"no ['thing']\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no returns false for non-null",
    "html": "",
    "body": "\n\t\texpect(await run(\"no 'thing'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no returns true for empty array",
    "html": "",
    "body": "\n\t\texpect(await run(\"no []\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no returns true for empty selector",
    "html": "",
    "body": "\n\t\texpect(await run(\"no .aClassThatDoesNotExist\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no returns true for null",
    "html": "",
    "body": "\n\t\texpect(await run(\"no null\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no with where and is not",
    "html": "",
    "body": "\n\t\tvar result = await run(\"no [1, 2, 3] where it is not 2\");\n\t\texpect(result).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no with where filters then checks emptiness",
    "html": "",
    "body": "\n\t\texpect(await run(\"no [1, 2, 3] where it > 5\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/no",
    "name": "no with where on DOM elements",
    "html": "<div id='box'><span class='a'>A</span><span class='b'>B</span></div><button _=\"on click if no <span/> in #box where it matches .c then put 'none' into #out else put 'found' into #out\">go</button><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box'><span class='a'>A</span><span class='b'>B</span></div>\" +\n\t\t\t\"<button _=\\\"on click if no <span/> in #box where it matches .c then put 'none' into #out else put 'found' into #out\\\">go</button>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"none\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/no",
    "name": "no with where returns false when matches exist",
    "html": "",
    "body": "\n\t\texpect(await run(\"no [1, 2, 3] where it > 1\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not has higher precedence than and",
    "html": "",
    "body": "\n\t\t// (not false) and true → true and true → true\n\t\texpect(await run(\"not false and true\")).toBe(true)\n\t\t// (not true) and true → false and true → false\n\t\texpect(await run(\"not true and true\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not has higher precedence than or",
    "html": "",
    "body": "\n\t\t// (not true) or true → false or true → true\n\t\texpect(await run(\"not true or true\")).toBe(true)\n\t\t// (not false) or false → true or false → true\n\t\texpect(await run(\"not false or false\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not inverts equality comparisons",
    "html": "",
    "body": "\n\t\texpect(await run(\"not (1 is 2)\")).toBe(true)\n\t\texpect(await run(\"not (1 is 1)\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not inverts false",
    "html": "",
    "body": "\n\t\texpect(await run(\"not false\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not inverts true",
    "html": "",
    "body": "\n\t\texpect(await run(\"not true\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not null and not undefined",
    "html": "",
    "body": "\n\t\texpect(await run(\"not null\")).toBe(true)\n\t\texpect(await run(\"not undefined\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not with numeric truthy/falsy values",
    "html": "",
    "body": "\n\t\texpect(await run(\"not 0\")).toBe(true)\n\t\texpect(await run(\"not 1\")).toBe(false)\n\t\texpect(await run(\"not 42\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "not with string truthy/falsy values",
    "html": "",
    "body": "\n\t\texpect(await run(\"not ''\")).toBe(true)\n\t\texpect(await run(\"not 'hello'\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/not",
    "name": "two nots make a true",
    "html": "",
    "body": "\n\t\texpect(await run(\"not not true\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/null",
    "name": "null literal work",
    "html": "",
    "body": "\n\t\tconst result = await run(\"null\")\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/numbers",
    "name": "handles numbers properly",
    "html": "",
    "body": "\n\t\texpect(await run(\"-1\")).toBe(-1)\n\t\texpect(await run(\"1\")).toBe(1)\n\t\texpect(await run(\"1.1\")).toBe(1.1)\n\t\texpect(await run(\"1e6\")).toBe(1e6)\n\t\texpect(await run(\"1e-6\")).toBe(1e-6)\n\t\texpect(await run(\"1.1e6\")).toBe(1.1e6)\n\t\texpect(await run(\"1.1e-6\")).toBe(1.1e-6)\n\t\texpect(await run(\"1234567890.1234567890\")).toBe(1234567890.123456789)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "allows trailing commas",
    "html": "",
    "body": "\n\t\texpect(await run(\"{foo:true, bar-baz:false,}\")).toEqual({ \"foo\": true, \"bar-baz\": false })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "deeply nested object literals work",
    "html": "",
    "body": "\n\t\texpect(await run(\"{a: {b: {c: 'deep'}}}\")).toEqual({ a: { b: { c: 'deep' } } })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "empty object literals work",
    "html": "",
    "body": "\n\t\texpect(await run(\"{}\")).toEqual({})\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "expressions work in object literal field names",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\twindow.foo = \"bar\"\n\t\t\twindow.bar = function () { return \"foo\" }\n\t\t\tconst r = _hyperscript(\"{[foo]:true, [bar()]:false}\")\n\t\t\tdelete window.foo\n\t\t\tdelete window.bar\n\t\t\treturn r\n\t\t})\n\t\texpect(result).toEqual({ bar: true, foo: false })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "hyphens work in object literal field names",
    "html": "",
    "body": "\n\t\texpect(await run(\"{-foo:true, bar-baz:false}\")).toEqual({ \"-foo\": true, \"bar-baz\": false })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "mixed field name styles in one literal",
    "html": "",
    "body": "\n\t\texpect(await run('{plain: 1, \"quoted\": 2, -dashed: 3}')).toEqual({\n\t\t\tplain: 1, quoted: 2, \"-dashed\": 3\n\t\t})\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "multi-field object literal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"{foo:true, bar:false}\")).toEqual({ foo: true, bar: false })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "nested object literals work",
    "html": "",
    "body": "\n\t\texpect(await run(\"{outer: {inner: 1}}\")).toEqual({ outer: { inner: 1 } })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "object literal values can be expressions",
    "html": "",
    "body": "\n\t\texpect(await run(\"{sum: 1 + 2, product: 3 * 4}\")).toEqual({ sum: 3, product: 12 })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "object literals can contain arrays",
    "html": "",
    "body": "\n\t\texpect(await run(\"{items: [1, 2, 3], count: 3}\")).toEqual({ items: [1, 2, 3], count: 3 })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "one field object literal works",
    "html": "",
    "body": "\n\t\texpect(await run(\"{foo:true}\")).toEqual({ foo: true })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/objectLiteral",
    "name": "strings work in object literal field names",
    "html": "",
    "body": "\n\t\texpect(await run('{\"foo\":true, \"bar\":false}')).toEqual({ foo: true, bar: false })\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "first works",
    "html": "",
    "body": "\n\t\texpect(await run(\"the first of [1, 2, 3]\")).toBe(1)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "first works w/ array-like",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"the first of .c1\") === document.getElementById(\"d1\")\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "first works w/ node",
    "html": "<div id='outerDiv'><div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outerDiv'><div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div></div>\"\n\t\t)\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('outerDiv')\n\t\t\treturn _hyperscript(\"the first of div\", { locals: { div: div } }) === document.getElementById(\"d1\")\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "is null safe",
    "html": "",
    "body": "\n\t\tconst result = await run(\"the first of null\")\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "last works",
    "html": "",
    "body": "\n\t\texpect(await run(\"the last of [1, 2, 3]\")).toBe(3)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "last works w/ array-like",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tconst result = await evaluate(() => {\n\t\t\treturn _hyperscript(\"the last of .c1\") === document.getElementById(\"d3\")\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/positionalExpression",
    "name": "last works w/ node",
    "html": "<div id='outerDiv'><div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outerDiv'><div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div></div>\"\n\t\t)\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('outerDiv')\n\t\t\treturn _hyperscript(\"the last of div\", { locals: { div: div } }) === document.getElementById(\"d3\")\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access basic attribute",
    "html": "<div id='pDiv' data-foo='bar'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' data-foo='bar'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\treturn _hyperscript(\"foo's [@data-foo]\", { locals: { foo: div } })\n\t\t})\n\t\texpect(result).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access basic properties",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo's foo\", { locals: { foo: { foo: \"foo\" } } })\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access basic style",
    "html": "<div id='pDiv' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' style='color:red'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\treturn _hyperscript(\"foo's *color\", { locals: { foo: div } })\n\t\t})\n\t\texpect(result).toBe(\"red\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access its properties",
    "html": "",
    "body": "\n\t\tconst result = await run(\"its foo\", { result: { foo: \"foo\" } })\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access multiple basic attributes",
    "html": "<div class='c1' data-foo='bar'></div><div class='c1' data-foo='bar'></div>",
    "body": "\n\t\tawait html(\"<div class='c1' data-foo='bar'></div><div class='c1' data-foo='bar'></div>\")\n\t\tconst result = await run(\".c1's [@data-foo]\")\n\t\texpect(result).toEqual([\"bar\", \"bar\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access multiple basic styles",
    "html": "<div class='c1' style='color:red'></div><div class='c1' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div class='c1' style='color:red'></div><div class='c1' style='color:red'></div>\")\n\t\tconst result = await run(\".c1's *color\")\n\t\texpect(result).toEqual([\"red\", \"red\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access my attribute",
    "html": "<div id='pDiv' data-foo='bar'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' data-foo='bar'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\treturn _hyperscript(\"my @data-foo\", { me: div })\n\t\t})\n\t\texpect(result).toBe(\"bar\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access my properties",
    "html": "",
    "body": "\n\t\tconst result = await run(\"my foo\", { me: { foo: \"foo\" } })\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access my style",
    "html": "<div id='pDiv' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' style='color:red'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\treturn _hyperscript(\"my *color\", { me: div })\n\t\t})\n\t\texpect(result).toBe(\"red\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on classrefs",
    "html": "<div class='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\"the display of .foo's style\")\n\t\texpect(result).toEqual([\"inline\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on classrefs 2",
    "html": "<div class='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\".foo's style's display\")\n\t\texpect(result).toEqual([\"inline\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on idrefs",
    "html": "<div id='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div id='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\"the display of #foo's style\")\n\t\texpect(result).toBe(\"inline\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on idrefs 2",
    "html": "<div id='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div id='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\"#foo's style's display\")\n\t\texpect(result).toBe(\"inline\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on queryrefs",
    "html": "<div class='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\"the display of <.foo/>'s style\")\n\t\texpect(result).toEqual([\"inline\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can access properties on queryrefs 2",
    "html": "<div class='foo' style='display: inline'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' style='display: inline'></div>\")\n\t\tconst result = await run(\"<.foo/>'s style's display\")\n\t\texpect(result).toEqual([\"inline\"])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can set basic attributes",
    "html": "<div id='pDiv' data-foo='bar'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' data-foo='bar'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\t_hyperscript(\"set foo's [@data-foo] to 'blah'\", { locals: { foo: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('pDiv').getAttribute(\"data-foo\"))\n\t\texpect(value).toBe(\"blah\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can set basic styles",
    "html": "<div id='pDiv' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' style='color:red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\t_hyperscript(\"set foo's *color to 'blue'\", { locals: { foo: div } })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('pDiv').style.color)\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can set multiple basic attributes",
    "html": "<div id='d1' class='c1' data-foo='bar'></div><div id='d2' class='c1' data-foo='bar'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' class='c1' data-foo='bar'></div><div id='d2' class='c1' data-foo='bar'></div>\")\n\t\tawait evaluate(() => _hyperscript(\"set .c1's [@data-foo] to 'blah'\"))\n\t\tconst v1 = await evaluate(() => document.getElementById('d1').getAttribute('data-foo'))\n\t\tconst v2 = await evaluate(() => document.getElementById('d2').getAttribute('data-foo'))\n\t\texpect(v1).toBe('blah')\n\t\texpect(v2).toBe('blah')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can set multiple basic styles",
    "html": "<div id='d1' class='c1' style='color:red'></div><div id='d2' class='c1' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' class='c1' style='color:red'></div><div id='d2' class='c1' style='color:red'></div>\")\n\t\tawait evaluate(() => _hyperscript(\"set .c1's *color to 'blue'\"))\n\t\tconst v1 = await evaluate(() => document.getElementById('d1').style.color)\n\t\tconst v2 = await evaluate(() => document.getElementById('d2').style.color)\n\t\texpect(v1).toBe('blue')\n\t\texpect(v2).toBe('blue')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "can set root styles",
    "html": "<div id='pDiv' style='color:red'></div>",
    "body": "\n\t\tawait html(\"<div id='pDiv' style='color:red'></div>\")\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.getElementById('pDiv')\n\t\t\t_hyperscript(\"set *color to 'blue'\", { me: div })\n\t\t})\n\t\tconst value = await evaluate(() => document.getElementById('pDiv').style.color)\n\t\texpect(value).toBe(\"blue\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "is null safe",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo's foo\")\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "its property is null safe",
    "html": "",
    "body": "\n\t\tconst result = await run(\"its foo\")\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/possessiveExpression",
    "name": "my property is null safe",
    "html": "",
    "body": "\n\t\tconst result = await run(\"my foo\")\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "can access basic properties",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo.foo\", { locals: { foo: { foo: \"foo\" } } })\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "chained property access (four levels)",
    "html": "",
    "body": "\n\t\tconst result = await run(\"a.b.c.d\", { locals: { a: { b: { c: { d: 42 } } } } })\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "chained property access (three levels)",
    "html": "",
    "body": "\n\t\tconst result = await run(\"a.b.c\", { locals: { a: { b: { c: \"deep\" } } } })\n\t\texpect(result).toBe(\"deep\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "is null safe",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo.foo\")\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "mixing dot and of forms",
    "html": "",
    "body": "\n\t\tconst result = await run(\"c of a.b\", { locals: { a: { b: { c: \"mixed\" } } } })\n\t\texpect(result).toBe(\"mixed\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "null-safe access through an undefined intermediate",
    "html": "",
    "body": "\n\t\tconst result = await run(\"a.b.c\", { locals: { a: {} } })\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "of form chains through multiple levels",
    "html": "",
    "body": "\n\t\tconst result = await run(\"c of b of a\", { locals: { a: { b: { c: \"deep\" } } } })\n\t\texpect(result).toBe(\"deep\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "of form works",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo of foo\", { locals: { foo: { foo: \"foo\" } } })\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "of form works w/ complex left side",
    "html": "",
    "body": "\n\t\tconst result = await run(\"bar.doh of foo\", {\n\t\t\tlocals: { foo: { bar: { doh: \"foo\" } } }\n\t\t})\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "of form works w/ complex right side",
    "html": "",
    "body": "\n\t\tconst result = await run(\"doh of foo.bar\", {\n\t\t\tlocals: { foo: { bar: { doh: \"foo\" } } }\n\t\t})\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "property access on function result",
    "html": "",
    "body": "\n\t\tawait evaluate(() => { window.makeObj = () => ({ name: 'hi' }) })\n\t\texpect(await run(\"makeObj().name\")).toBe('hi')\n\t\tawait evaluate(() => { delete window.makeObj })\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/propertyAccess",
    "name": "works properly w/ boolean properties",
    "html": "<input class='cb' type='checkbox' checked='checked'/> <input class='cb' type='checkbox'/> ",
    "body": "\n\t\tawait html(\"<input class='cb' type='checkbox' checked='checked'/> <input class='cb' type='checkbox'/> \")\n\t\tconst result = await run(\".cb.checked\")\n\t\texpect(result).toEqual([true, false])\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works",
    "html": "<div class='c1'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<.c1/>\")).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w no match",
    "html": "",
    "body": "\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<.badClassThatDoesNotHaveAnyElements/>\")).length)\n\t\texpect(len).toBe(0)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w properties w/ strings",
    "html": "<div class='c1'></div><div foo='bar' class='c2'></div><div class='c3'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div><div foo='bar' class='c2'></div><div class='c3'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<[foo='bar']/>\")).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w/ div selector",
    "html": "<div class='c1'></div><div class='c2'></div><div class='c3'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div><div class='c2'></div><div class='c3'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<div.c1/>\")).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w/ funny selector",
    "html": "<div title='t1'></div><div title='t2'></div><div title='t3'></div>",
    "body": "\n\t\tawait html(\"<div title='t1'></div><div title='t2'></div><div title='t3'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<:active/>\")).length)\n\t\texpect(len).toBe(0)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w/ multiple matches",
    "html": "<div class='c1'></div><div class='c1'></div><div class='c1'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div><div class='c1'></div><div class='c1'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<.c1/>\")).length)\n\t\texpect(len).toBe(3)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "basic queryRef works w/ properties",
    "html": "<div title='t1'></div><div title='t2'></div><div title='t3'></div>",
    "body": "\n\t\tawait html(\"<div title='t1'></div><div title='t2'></div><div title='t3'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<[title=t2]/>\")).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "can interpolate elements into queries",
    "html": "<div class='a'></div><div class='b'></div>",
    "body": "\n\t\tawait html(\"<div class='a'></div><div class='b'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst a = document.querySelector('#work-area .a')\n\t\t\tconst value = _hyperscript(\"<${a} + div/>\", { locals: { a } })\n\t\t\treturn Array.from(value)[0] === document.querySelector('#work-area .b')\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "queryRef w/ $ no curlies works",
    "html": "<div id='t1'></div><div id='t2'></div><div id='t3'></div>",
    "body": "\n\t\tawait html(\"<div id='t1'></div><div id='t2'></div><div id='t3'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst value = _hyperscript(\"<#$id/>\", { locals: { id: \"t2\" } })\n\t\t\treturn Array.from(value)[0] === document.getElementById(\"t2\")\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "queryRef w/ $ works",
    "html": "<div class='c1'></div><div foo='bar' class='c2'></div><div class='c3'></div>",
    "body": "\n\t\tawait html(\"<div class='c1'></div><div foo='bar' class='c2'></div><div class='c3'></div>\")\n\t\tconst len = await evaluate(() => {\n\t\t\treturn Array.from(_hyperscript(\"<[foo='${x}']/>\", { locals: { x: \"bar\" } })).length\n\t\t})\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "queryRefs support colons properly",
    "html": "<input class='foo' type='checkbox' checked='checked'>",
    "body": "\n\t\tawait html(\"<input class='foo' type='checkbox' checked='checked'>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript(\"<input.foo:checked/>\")).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "queryRefs support dollar properly",
    "html": "<div title='little flower'></div>",
    "body": "\n\t\tawait html(\"<div title='little flower'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript('<[title$=\"flower\"]/>')).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/queryRef",
    "name": "queryRefs support tildes properly",
    "html": "<div title='little flower'></div>",
    "body": "\n\t\tawait html(\"<div title='little flower'></div>\")\n\t\tconst len = await evaluate(() => Array.from(_hyperscript('<[title~=\"flower\"]/>')).length)\n\t\texpect(len).toBe(1)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "can access property of next element with possessive",
    "html": "<div id=\"d1\"></div><div id=\"d2\">hello</div>",
    "body": "\n\t\tawait html('<div id=\"d1\"></div><div id=\"d2\">hello</div>')\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/>'s textContent\", {me: document.getElementById('d1')}))\n\t\texpect(result).toBe('hello')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "can access property of previous element with possessive",
    "html": "<div id=\"d1\">world</div><div id=\"d2\"></div>",
    "body": "\n\t\tawait html('<div id=\"d1\">world</div><div id=\"d2\"></div>')\n\t\tlet result = await evaluate(() => _hyperscript(\"the previous <div/>'s textContent\", {me: document.getElementById('d2')}))\n\t\texpect(result).toBe('world')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "can access style of next element with possessive",
    "html": "<div id=\"d1\"></div><div id=\"d2\" style=\"color: red\"></div>",
    "body": "\n\t\tawait html('<div id=\"d1\"></div><div id=\"d2\" style=\"color: red\"></div>')\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/>'s *color\", {me: document.getElementById('d1')}))\n\t\texpect(result).toBe('red')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "can write to next element with put command",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\tvar wa = document.getElementById('work-area');\n\t\t\twa.innerHTML = '<div id=\"d1\"></div><div id=\"d2\">original</div>';\n\t\t\twa.querySelector('#d1').setAttribute('_', \"on click put 'updated' into the next <div/>'s textContent\");\n\t\t\t_hyperscript.process(wa);\n\t\t});\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveText('updated');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly among siblings",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/> from #d1\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d2\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d3\"))\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly among siblings with wrapping",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/> from #d1 within the #work-area with wrapping\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d2 within the #work-area with wrapping\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d3 within the #work-area with wrapping\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly with array-like",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/> from #d1 in .c1\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d2 in .c1\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d3 in .c1\"))\n\t\texpect(result).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly with array-like and wrap",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the next <div/> from #d1 in .c1 with wrapping\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d2 in .c1  with wrapping\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next <div/> from #d3 in .c1  with wrapping\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly with array-like no match",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d1 in .c1\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d2 in .c1\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d3 in .c1\"))).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "next works properly with array-like no match and wrap",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d1 in .c1 with wrapping\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d2 in .c1 with wrapping\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the next <h1/> from #d3 in .c1 with wrapping\"))).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly among siblings",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the previous <div/> from #d1 within #work-area\"))\n\t\texpect(result).toBeUndefined()\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d2\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d3\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly among siblings with wrapping",
    "html": "<div id='d1' class='c1'></div><div id='d2' class='c1'></div><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the previous <div/> from #d1 within the #work-area with wrapping\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d2 within the #work-area with wrapping\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d3 within the #work-area with wrapping\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly with array-like",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <div/> from #d1 in .c1\"))).toBeUndefined()\n\t\tlet result = await evaluate(() => _hyperscript(\"the previous <div/> from #d2 in .c1\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d3 in .c1\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly with array-like and wrap",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the previous <div/> from #d1 in .c1 with wrapping\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d2 in .c1  with wrapping\") === document.getElementById(\"d1\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the previous <div/> from #d3 in .c1  with wrapping\") === document.getElementById(\"d2\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly with array-like no match",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d1 in .c1\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d2 in .c1\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d3 in .c1\"))).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "previous works properly with array-like no match and wrap",
    "html": "<div id='d1' class='c1'></div><p class='c1'></p><div id='d2' class='c1'></div><p class='c1'></p><div id='d3' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d2' class='c1'></div><p class='c1'></p>\" +\n\t\t\t\"<div id='d3' class='c1'></div>\"\n\t\t)\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d1 in .c1 with wrapping\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d2 in .c1 with wrapping\"))).toBeUndefined()\n\t\texpect(await evaluate(() => _hyperscript(\"the previous <h1/> from #d3 in .c1 with wrapping\"))).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "properly constrains via the within modifier",
    "html": "<div id='d1' class='c1'><div id='d2' class='c1'></div><div id='d3' class='c1'></div></div><div id='d4' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'><div id='d2' class='c1'></div><div id='d3' class='c1'></div></div><div id='d4' class='c1'></div>\"\n\t\t)\n\t\tlet result = await evaluate(() => _hyperscript(\"the next .c1 from #d2 within #d1\") === document.getElementById(\"d3\"))\n\t\texpect(result).toBe(true)\n\t\tresult = await evaluate(() => _hyperscript(\"the next .c1 from #d3 within #d1\"))\n\t\texpect(result).toBeUndefined()\n\t\tresult = await evaluate(() => _hyperscript(\"the next .c1 from #d3 within the #work-area\") === document.getElementById(\"d4\"))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative next works properly among siblings w/ class",
    "html": "<div id='d1' _='on click add .foo to next .c1' class='c1'></div><div id='d2' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click add .foo to next .c1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d2').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative next works properly among siblings w/ query",
    "html": "<div id='d1' _='on click add .foo to next <div/>' class='c1'></div><div id='d2' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click add .foo to next <div/>' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d2').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative next works properly among siblings w/ query & class",
    "html": "<div id='d1' _='on click add .foo to next <div.c1/>' class='c1'></div><div id='d2' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click add .foo to next <div.c1/>' class='c1'></div>\" +\n\t\t\t\"<div id='d2' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d2').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative previous works properly among siblings w/ class",
    "html": "<div id='d1' class='c1'></div><div id='d2' _='on click add .foo to previous .c1' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' _='on click add .foo to previous .c1' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d2').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d1').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative previous works properly among siblings w/ query",
    "html": "<div id='d1' class='c1'></div><div id='d2' _='on click add .foo to previous <div/>' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' _='on click add .foo to previous <div/>' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d2').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d1').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/relativePositionalExpression",
    "name": "relative previous works properly among siblings w/ query & class",
    "html": "<div id='d1' class='c1'></div><div id='d2' _='on click add .foo to previous <div.c1/>' class='c1'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' class='c1'></div>\" +\n\t\t\t\"<div id='d2' _='on click add .foo to previous <div.c1/>' class='c1'></div>\"\n\t\t)\n\t\tawait find('#d2').dispatchEvent('click')\n\t\tawait expect.poll(() => evaluate(() => document.getElementById('d1').classList.contains('foo'))).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/some",
    "name": "some returns false for empty array",
    "html": "",
    "body": "\n\t\texpect(await run(\"some []\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/some",
    "name": "some returns false for empty selector",
    "html": "",
    "body": "\n\t\texpect(await run(\"some .aClassThatDoesNotExist\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/some",
    "name": "some returns false for null",
    "html": "",
    "body": "\n\t\texpect(await run(\"some null\")).toBe(false)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/some",
    "name": "some returns true for filled array",
    "html": "",
    "body": "\n\t\texpect(await run(\"some ['thing']\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/some",
    "name": "some returns true for non-null",
    "html": "",
    "body": "\n\t\texpect(await run(\"some 'thing'\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/some",
    "name": "some returns true for nonempty selector",
    "html": "",
    "body": "\n\t\texpect(await run(\"some <html/>\")).toBe(true)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "joins an array with delimiter",
    "html": "",
    "body": "\n\t\tvar result = await run(`return [\"a\", \"b\", \"c\"] joined by \", \"`);\n\t\texpect(result).toBe(\"a, b, c\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "joins with empty string",
    "html": "",
    "body": "\n\t\tvar result = await run(`return [\"x\", \"y\", \"z\"] joined by \"\"`);\n\t\texpect(result).toBe(\"xyz\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "split then mapped then joined",
    "html": "",
    "body": "\n\t\tvar result = await run(`return \"hello world\" split by \" \" mapped to its length joined by \",\"`);\n\t\texpect(result).toBe(\"5,5\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "split then sorted then joined",
    "html": "",
    "body": "\n\t\tvar result = await run(`return \"banana,apple,cherry\" split by \",\" sorted by it joined by \", \"`);\n\t\texpect(result).toBe(\"apple, banana, cherry\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "split then where then joined",
    "html": "",
    "body": "\n\t\tvar result = await run(`return \"a,,b,,c\" split by \",\" where it is not \"\" joined by \"-\"`);\n\t\texpect(result).toBe(\"a-b-c\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "splits a string by delimiter",
    "html": "",
    "body": "\n\t\tvar result = await run(`return \"a,b,c\" split by \",\"`);\n\t\texpect(result).toEqual([\"a\", \"b\", \"c\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/splitJoin",
    "name": "splits by whitespace",
    "html": "",
    "body": "\n\t\tvar result = await run(`return \"hello world\" split by \" \"`);\n\t\texpect(result).toEqual([\"hello\", \"world\"]);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/stringPostfix",
    "name": "handles basic postfix strings properly",
    "html": "",
    "body": "\n\t\texpect(await run(\"1em\")).toBe(\"1em\")\n\t\texpect(await run(\"1px\")).toBe(\"1px\")\n\t\texpect(await run(\"-1px\")).toBe(\"-1px\")\n\t\texpect(await run(\"100%\")).toBe(\"100%\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/stringPostfix",
    "name": "handles basic postfix strings with spaces properly",
    "html": "",
    "body": "\n\t\texpect(await run(\"1 em\")).toBe(\"1em\")\n\t\texpect(await run(\"1 px\")).toBe(\"1px\")\n\t\texpect(await run(\"100 %\")).toBe(\"100%\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/stringPostfix",
    "name": "handles expression roots properly",
    "html": "",
    "body": "\n\t\texpect(await run(\"(0 + 1) em\")).toBe(\"1em\")\n\t\texpect(await run(\"(0 + 1) px\")).toBe(\"1px\")\n\t\texpect(await run(\"(100 + 0) %\")).toBe(\"100%\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "handles strings properly",
    "html": "",
    "body": "\n\t\tvar result = await run('\"foo\"');\n\t\texpect(result).toBe(\"foo\");\n\n\t\tresult = await run('\"fo\\'o\"');\n\t\texpect(result).toBe(\"fo'o\");\n\n\t\tresult = await run(\"'foo'\");\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "should handle back slashes in non-template content",
    "html": "",
    "body": "\n\t\tvar result = await run(\"`https://${foo}`\", {locals: {foo: 'bar'}});\n\t\texpect(result).toBe('https://bar');\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "should handle strings with tags and quotes",
    "html": "",
    "body": "\n\t\tvar record = {\n\t\t\tname: \"John Connor\",\n\t\t\tage: 21,\n\t\t\tfavouriteColour: \"bleaux\",\n\t\t};\n\t\tvar result = await run(\n\t\t\t'`<div age=\"${record.age}\" style=\"color:${record.favouriteColour}\">${record.name}</div>`',\n\t\t\t{locals: {record: record}}\n\t\t);\n\t\texpect(result).toBe('<div age=\"21\" style=\"color:bleaux\">John Connor</div>');\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "string templates preserve white space",
    "html": "",
    "body": "\n\t\tvar result = await run(\"` ${1 + 2} ${1 + 2} `\");\n\t\texpect(result).toBe(\" 3 3 \");\n\n\t\tresult = await run(\"`${1 + 2} ${1 + 2} `\");\n\t\texpect(result).toBe(\"3 3 \");\n\n\t\tresult = await run(\"`${1 + 2}${1 + 2} `\");\n\t\texpect(result).toBe(\"33 \");\n\n\t\tresult = await run(\"`${1 + 2} ${1 + 2}`\");\n\t\texpect(result).toBe(\"3 3\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "string templates work properly",
    "html": "",
    "body": "\n\t\tvar result = await run(\"`$1`\");\n\t\texpect(result).toBe(\"1\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "string templates work properly w braces",
    "html": "",
    "body": "\n\t\tvar result = await run(\"`${1 + 2}`\");\n\t\texpect(result).toBe(\"3\");\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/strings",
    "name": "string templates work w/ props",
    "html": "",
    "body": "\n\t\tawait evaluate(() => window.foo = \"foo\");\n\t\tvar result = await run(\"`$window.foo`\");\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/strings",
    "name": "string templates work w/ props w/ braces",
    "html": "",
    "body": "\n\t\tawait evaluate(() => window.foo = \"foo\");\n\t\tvar result = await run(\"`${window.foo}`\");\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/styleRef",
    "name": "basic style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"*color\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"red\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*text-align\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*width\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*height\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*bad-prop\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/styleRef",
    "name": "calculated of style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"*computed-color of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"rgb(255, 0, 0)\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-text-align of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-width of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-height of it\", { result: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"0px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-bad-prop of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/styleRef",
    "name": "calculated possessive style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"my *computed-color\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"rgb(255, 0, 0)\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *computed-text-align\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *computed-width\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"its *computed-height\", { result: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"0px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *computed-bad-prop\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/styleRef",
    "name": "calculated style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"*computed-color\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"rgb(255, 0, 0)\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-text-align\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-width\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-height\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"0px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*computed-bad-prop\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"\")\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/styleRef",
    "name": "of style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"*color of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"red\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*text-align of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*width of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*height of it\", { result: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"*bad-prop of me\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/styleRef",
    "name": "possessive style ref works",
    "html": "<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>",
    "body": "\n\t\tawait html(\"<div id='sDiv' style='color: red; text-align: center; width: 10px'></div>\")\n\n\t\tlet value = await evaluate(() => _hyperscript(\"my *color\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"red\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *text-align\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"center\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *width\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"10px\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"its *height\", { result: document.getElementById('sDiv') }))\n\t\texpect(value).toBe(\"\")\n\n\t\tvalue = await evaluate(() => _hyperscript(\"my *bad-prop\", { me: document.getElementById('sDiv') }))\n\t\texpect(value).toBeUndefined()\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "expressions/symbol",
    "name": "resolves global context properly",
    "html": "",
    "body": "\n\t\tconst result = await evaluate(() => {\n\t\t\tconst r = _hyperscript(\"document\", { locals: { foo: 42 } })\n\t\t\treturn r === document\n\t\t})\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/symbol",
    "name": "resolves local context properly",
    "html": "",
    "body": "\n\t\tconst result = await run(\"foo\", { locals: { foo: 42 } })\n\t\texpect(result).toBe(42)\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/typecheck",
    "name": "can do basic non-string typecheck failure",
    "html": "",
    "body": "\n\t\tconst msg = await evaluate(() => {\n\t\t\ttry {\n\t\t\t\t_hyperscript(\"true : String\")\n\t\t\t\treturn null\n\t\t\t} catch (e) {\n\t\t\t\treturn e.message\n\t\t\t}\n\t\t})\n\t\texpect(msg).toMatch(/^Typecheck failed!/)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "expressions/typecheck",
    "name": "can do basic string non-null typecheck",
    "html": "",
    "body": "\n\t\tconst result = await run(\"'foo' : String!\")\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/typecheck",
    "name": "can do basic string typecheck",
    "html": "",
    "body": "\n\t\tconst result = await run(\"'foo' : String\")\n\t\texpect(result).toBe(\"foo\")\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/typecheck",
    "name": "can do null as string typecheck",
    "html": "",
    "body": "\n\t\tconst result = await run(\"null : String\")\n\t\texpect(result).toBeNull()\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "expressions/typecheck",
    "name": "null causes null safe string check to fail",
    "html": "",
    "body": "\n\t\tconst msg = await evaluate(() => {\n\t\t\ttry {\n\t\t\t\t_hyperscript(\"null : String!\")\n\t\t\t\treturn null\n\t\t\t} catch (e) {\n\t\t\t\treturn e.message\n\t\t\t}\n\t\t})\n\t\texpect(msg).toMatch(/^Typecheck failed!/)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "ext/component",
    "name": "applies _ hyperscript to component instance",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-init\" _=\"init set ^msg to 'initialized'\">\n\t\t\t\t<span>${}{^msg}</span>\n\t\t\t</script>\n\t\t\t<test-init></test-init>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-init\" _=\"init set ^msg to 'initialized'\">\n\t\t\t\t<span>${\"\\x24\"}{^msg}</span>\n\t\t\t</script>\n\t\t\t<test-init></test-init>\n\t\t`)\n\t\tawait expect.poll(() => find('test-init span').textContent()).toBe('initialized')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "attrs bind is bidirectional - inner changes flow outward",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args-bidir\" _=\"bind ^count to attrs.count\">\n\t\t\t\t<span>${}{^count}</span>\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t</script>\n\t\t\t<test-args-bidir count=\"$count\"></test-args-bidir>\n\t\t\t<p _=\"live put $count into me\"></p>\n\t\t",
    "body": "\n\t\tawait run(\"set $count to 10\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args-bidir\" _=\"bind ^count to attrs.count\">\n\t\t\t\t<span>${\"\\x24\"}{^count}</span>\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t</script>\n\t\t\t<test-args-bidir count=\"$count\"></test-args-bidir>\n\t\t\t<p _=\"live put $count into me\"></p>\n\t\t`)\n\t\tawait expect.poll(() => find('test-args-bidir span').textContent()).toBe('10')\n\t\tawait expect.poll(() => find('p').textContent()).toBe('10')\n\t\tawait find('test-args-bidir button').click()\n\t\t// Inner ^count should update\n\t\tawait expect.poll(() => find('test-args-bidir span').textContent()).toBe('11')\n\t\t// Outer $count should also update via attrs write-back\n\t\tawait expect.poll(() => find('p').textContent(), { timeout: 2000 }).toBe('11')\n\t\tawait evaluate(() => { delete window.$count })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "attrs evaluates attribute as hyperscript in parent scope",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args\" _=\"init set ^list to attrs.items\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^list\n\t\t\t\t\t<li>${}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t\t<test-args items=\"$stuff\"></test-args>\n\t\t",
    "body": "\n\t\tawait run(\"set $stuff to ['a', 'b', 'c']\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args\" _=\"init set ^list to attrs.items\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^list\n\t\t\t\t\t<li>${\"\\x24\"}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t\t<test-args items=\"$stuff\"></test-args>\n\t\t`)\n\t\tawait expect.poll(() => find('test-args li').count()).toBe(3)\n\t\tawait expect.poll(() => find('test-args li').first().textContent()).toBe('a')\n\t\tawait evaluate(() => { delete window.$stuff })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "attrs works with bind for reactive pass-through",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args-bind\" _=\"bind ^val to attrs.count\">\n\t\t\t\t<span>${}{^val}</span>\n\t\t\t</script>\n\t\t\t<test-args-bind count=\"$count\"></test-args-bind>\n\t\t\t<button _=\"on click increment $count\">+</button>\n\t\t",
    "body": "\n\t\tawait run(\"set $count to 10\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-args-bind\" _=\"bind ^val to attrs.count\">\n\t\t\t\t<span>${\"\\x24\"}{^val}</span>\n\t\t\t</script>\n\t\t\t<test-args-bind count=\"$count\"></test-args-bind>\n\t\t\t<button _=\"on click increment $count\">+</button>\n\t\t`)\n\t\tawait expect.poll(() => find('test-args-bind span').textContent()).toBe('10')\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('test-args-bind span').textContent()).toBe('11')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "bind keeps ^var in sync with attribute changes",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-bind-attr\" _=\"bind ^count to @data-count\">\n\t\t\t\t<span>${}{^count}</span>\n\t\t\t</script>\n\t\t\t<test-bind-attr data-count=\"5\"></test-bind-attr>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-bind-attr\" _=\"bind ^count to @data-count\">\n\t\t\t\t<span>${\"\\x24\"}{^count}</span>\n\t\t\t</script>\n\t\t\t<test-bind-attr data-count=\"5\"></test-bind-attr>\n\t\t`)\n\t\tawait expect.poll(() => find('test-bind-attr span').textContent()).toBe('5')\n\t\tawait evaluate(() => document.querySelector('test-bind-attr').setAttribute('data-count', '99'))\n\t\tawait expect.poll(() => find('test-bind-attr span').textContent()).toBe('99')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "blocks processing of inner hyperscript until render",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-block\" _=\"init set ^msg to 'ready'\">\n\t\t\t\t<span _=\"on click put ^msg into me\">click me</span>\n\t\t\t</script>\n\t\t\t<test-block></test-block>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-block\" _=\"init set ^msg to 'ready'\">\n\t\t\t\t<span _=\"on click put ^msg into me\">click me</span>\n\t\t\t</script>\n\t\t\t<test-block></test-block>\n\t\t`)\n\t\tawait expect.poll(() => find('test-block span').textContent()).toBe('click me')\n\t\tawait find('test-block span').click()\n\t\tawait expect.poll(() => find('test-block span').textContent()).toBe('ready')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "component isolation prevents ^var leaking inward",
    "html": "\n\t\t\t<div _=\"init set ^leaked to 'should-not-see'\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-isolated\" _=\"init set ^internal to 'component-only'\">\n\t\t\t\t\t<span _=\"init if ^leaked is not undefined put 'leaked!' into me else put ^internal into me\">waiting</span>\n\t\t\t\t</script>\n\t\t\t\t<test-isolated></test-isolated>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^leaked to 'should-not-see'\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-isolated\" _=\"init set ^internal to 'component-only'\">\n\t\t\t\t\t<span _=\"init if ^leaked is not undefined put 'leaked!' into me else put ^internal into me\">waiting</span>\n\t\t\t\t</script>\n\t\t\t\t<test-isolated></test-isolated>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('test-isolated span').textContent()).toBe('component-only')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "does not process slotted _ attributes prematurely",
    "html": "\n\t\t\t<div _=\"init set ^x to 42\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-slot-hs\">\n\t\t\t\t\t<div class=\"wrap\"><slot/></div>\n\t\t\t\t</script>\n\t\t\t\t<test-slot-hs>\n\t\t\t\t\t<span _=\"on click put ^x into me\">before</span>\n\t\t\t\t</test-slot-hs>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^x to 42\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-slot-hs\">\n\t\t\t\t\t<div class=\"wrap\"><slot/></div>\n\t\t\t\t</script>\n\t\t\t\t<test-slot-hs>\n\t\t\t\t\t<span _=\"on click put ^x into me\">before</span>\n\t\t\t\t</test-slot-hs>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('test-slot-hs span').textContent()).toBe('before')\n\t\tawait find('test-slot-hs span').click()\n\t\tawait expect.poll(() => find('test-slot-hs span').textContent()).toBe('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "extracts <style> blocks and wraps them in @scope",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-styled\">\n\t\t\t\t<style>\n\t\t\t\t\t:scope { display: block; }\n\t\t\t\t\t.inner { color: rgb(11, 22, 33); }\n\t\t\t\t</style>\n\t\t\t\t<span class=\"inner\">styled</span>\n\t\t\t</script>\n\t\t\t<test-styled></test-styled>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-styled\">\n\t\t\t\t<style>\n\t\t\t\t\t:scope { display: block; }\n\t\t\t\t\t.inner { color: rgb(11, 22, 33); }\n\t\t\t\t</style>\n\t\t\t\t<span class=\"inner\">styled</span>\n\t\t\t</script>\n\t\t\t<test-styled></test-styled>\n\t\t`)\n\t\t// The wrapped style tag should sit immediately after the template\n\t\tconst styleText = await page.evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('script[component=\"test-styled\"]')\n\t\t\tconst next = tmpl && tmpl.nextElementSibling\n\t\t\treturn next && next.tagName === 'STYLE' ? next.textContent : null\n\t\t})\n\t\texpect(styleText).toContain('@scope (test-styled)')\n\t\texpect(styleText).toContain('.inner')\n\t\t// And the script's text content no longer has a <style>\n\t\tconst hasStyle = await page.evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('script[component=\"test-styled\"]')\n\t\t\treturn tmpl.textContent.includes('<style>')\n\t\t})\n\t\texpect(hasStyle).toBe(false)\n\t\t// Style actually applies to the rendered descendant\n\t\tawait expect.poll(async () => {\n\t\t\treturn await page.evaluate(() => {\n\t\t\t\tconst el = document.querySelector('test-styled .inner')\n\t\t\t\treturn el && getComputedStyle(el).color\n\t\t\t})\n\t\t}).toBe('rgb(11, 22, 33)')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "processes _ on inner elements",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-inner\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count then put ^count into the next <span/>\">+</button>\n\t\t\t\t<span>0</span>\n\t\t\t</script>\n\t\t\t<test-inner></test-inner>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-inner\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count then put ^count into the next <span/>\">+</button>\n\t\t\t\t<span>0</span>\n\t\t\t</script>\n\t\t\t<test-inner></test-inner>\n\t\t`)\n\t\tawait expect.poll(() => find('test-inner span').textContent()).toBe('0')\n\t\tawait find('test-inner button').click()\n\t\tawait expect.poll(() => find('test-inner span').textContent()).toBe('1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "reactively updates template expressions",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-reactive\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t\t<span>Count: ${}{^count}</span>\n\t\t\t</script>\n\t\t\t<test-reactive></test-reactive>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-reactive\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count\">+</button>\n\t\t\t\t<span>Count: ${\"\\x24\"}{^count}</span>\n\t\t\t</script>\n\t\t\t<test-reactive></test-reactive>\n\t\t`)\n\t\tawait expect.poll(() => find('test-reactive span').textContent()).toBe('Count: 0')\n\t\tawait find('test-reactive button').click()\n\t\tawait expect.poll(() => find('test-reactive span').textContent()).toBe('Count: 1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "reads attributes via @",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-attrs\" _=\"init set ^val to @data-start as Int\">\n\t\t\t\t<span>${}{^val}</span>\n\t\t\t</script>\n\t\t\t<test-attrs data-start=\"42\"></test-attrs>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-attrs\" _=\"init set ^val to @data-start as Int\">\n\t\t\t\t<span>${\"\\x24\"}{^val}</span>\n\t\t\t</script>\n\t\t\t<test-attrs data-start=\"42\"></test-attrs>\n\t\t`)\n\t\tawait expect.poll(() => find('test-attrs span').textContent()).toBe('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "registers a custom element from a template",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-hello\">\n\t\t\t\t<span>Hello World</span>\n\t\t\t</script>\n\t\t\t<test-hello></test-hello>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-hello\">\n\t\t\t\t<span>Hello World</span>\n\t\t\t</script>\n\t\t\t<test-hello></test-hello>\n\t\t`)\n\t\tawait expect.poll(() => find('test-hello span').textContent()).toBe('Hello World')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "renders template expressions",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-greet\">\n\t\t\t\t<span>Hello ${}{$name}!</span>\n\t\t\t</script>\n\t\t\t<test-greet></test-greet>\n\t\t",
    "body": "\n\t\tawait run(\"set $name to 'hyperscript'\")\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-greet\">\n\t\t\t\t<span>Hello ${\"\\x24\"}{$name}!</span>\n\t\t\t</script>\n\t\t\t<test-greet></test-greet>\n\t\t`)\n\t\tawait expect.poll(() => find('test-greet span').textContent()).toBe('Hello hyperscript!')\n\t\tawait evaluate(() => { delete window.$name })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "slotted content resolves ^var from outer scope, not component scope",
    "html": "\n\t\t\t<div _=\"init set ^outer to 'from-outside'\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-scope-slot\" _=\"init set ^outer to 'from-component'\">\n\t\t\t\t\t<div class=\"inner\"><slot/></div>\n\t\t\t\t</script>\n\t\t\t\t<test-scope-slot>\n\t\t\t\t\t<span _=\"init put ^outer into me\">waiting</span>\n\t\t\t\t</test-scope-slot>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div _=\"init set ^outer to 'from-outside'\">\n\t\t\t\t<script type=\"text/hyperscript-template\" component=\"test-scope-slot\" _=\"init set ^outer to 'from-component'\">\n\t\t\t\t\t<div class=\"inner\"><slot/></div>\n\t\t\t\t</script>\n\t\t\t\t<test-scope-slot>\n\t\t\t\t\t<span _=\"init put ^outer into me\">waiting</span>\n\t\t\t\t</test-scope-slot>\n\t\t\t</div>\n\t\t`)\n\t\tawait expect.poll(() => find('test-scope-slot span').textContent()).toBe('from-outside')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "substitutes slot content into template",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-card\">\n\t\t\t\t<div class=\"card\"><slot/></div>\n\t\t\t</script>\n\t\t\t<test-card><p>Hello from slot</p></test-card>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-card\">\n\t\t\t\t<div class=\"card\"><slot/></div>\n\t\t\t</script>\n\t\t\t<test-card><p>Hello from slot</p></test-card>\n\t\t`)\n\t\tawait expect.poll(() => find('test-card .card p').textContent()).toBe('Hello from slot')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "supports #for loops in template",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-loop\" _=\"init set ^items to ['a', 'b', 'c']\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li>${}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t\t<test-loop></test-loop>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-loop\" _=\"init set ^items to ['a', 'b', 'c']\">\n\t\t\t\t<ul>\n\t\t\t\t#for item in ^items\n\t\t\t\t\t<li>${\"\\x24\"}{item}</li>\n\t\t\t\t#end\n\t\t\t\t</ul>\n\t\t\t</script>\n\t\t\t<test-loop></test-loop>\n\t\t`)\n\t\tawait expect.poll(() => find('test-loop li').count()).toBe(3)\n\t\tawait expect.poll(() => find('test-loop li').first().textContent()).toBe('a')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "supports #if conditionals in template",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-cond\" _=\"init set ^show to true\">\n\t\t\t\t#if ^show\n\t\t\t\t\t<span>visible</span>\n\t\t\t\t#end\n\t\t\t</script>\n\t\t\t<test-cond></test-cond>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-cond\" _=\"init set ^show to true\">\n\t\t\t\t#if ^show\n\t\t\t\t\t<span>visible</span>\n\t\t\t\t#end\n\t\t\t</script>\n\t\t\t<test-cond></test-cond>\n\t\t`)\n\t\tawait expect.poll(() => find('test-cond span').textContent()).toBe('visible')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "supports multiple independent instances",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-multi\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count then put ^count into the next <span/>\">+</button>\n\t\t\t\t<span>0</span>\n\t\t\t</script>\n\t\t\t<test-multi id=\"a\"></test-multi>\n\t\t\t<test-multi id=\"b\"></test-multi>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-multi\" _=\"init set ^count to 0\">\n\t\t\t\t<button _=\"on click increment ^count then put ^count into the next <span/>\">+</button>\n\t\t\t\t<span>0</span>\n\t\t\t</script>\n\t\t\t<test-multi id=\"a\"></test-multi>\n\t\t\t<test-multi id=\"b\"></test-multi>\n\t\t`)\n\t\tawait find('#a button').click()\n\t\tawait expect.poll(() => find('#a span').textContent()).toBe('1')\n\t\tawait expect.poll(() => find('#b span').textContent()).toBe('0')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/component",
    "name": "supports named slots",
    "html": "\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-named-slot\">\n\t\t\t\t<header><slot name=\"title\"></slot></header>\n\t\t\t\t<main><slot/></main>\n\t\t\t\t<footer><slot name=\"footer\"></slot></footer>\n\t\t\t</script>\n\t\t\t<test-named-slot>\n\t\t\t\t<h1 slot=\"title\">My Title</h1>\n\t\t\t\t<p>Default content</p>\n\t\t\t\t<span slot=\"footer\">Footer text</span>\n\t\t\t</test-named-slot>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript-template\" component=\"test-named-slot\">\n\t\t\t\t<header><slot name=\"title\"></slot></header>\n\t\t\t\t<main><slot/></main>\n\t\t\t\t<footer><slot name=\"footer\"></slot></footer>\n\t\t\t</script>\n\t\t\t<test-named-slot>\n\t\t\t\t<h1 slot=\"title\">My Title</h1>\n\t\t\t\t<p>Default content</p>\n\t\t\t\t<span slot=\"footer\">Footer text</span>\n\t\t\t</test-named-slot>\n\t\t`)\n\t\tawait expect.poll(() => find('test-named-slot header h1').textContent()).toBe('My Title')\n\t\tawait expect.poll(() => find('test-named-slot main p').textContent()).toBe('Default content')\n\t\tawait expect.poll(() => find('test-named-slot footer span').textContent()).toBe('Footer text')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/eventsource",
    "name": "as json decodes data",
    "html": "\n            <div id=\"name\"></div>\n            <div id=\"age\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource JsonSSE from \"/sse\"\n                    on \"update\" as json\n                        put it.name into #name\n                        put it.age into #age\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'update', data: '{\"name\":\"Alice\",\"age\":30}' },\n        ]);\n        await html(`\n            <div id=\"name\"></div>\n            <div id=\"age\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource JsonSSE from \"/sse\"\n                    on \"update\" as json\n                        put it.name into #name\n                        put it.age into #age\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#name').textContent()).toBe('Alice');\n        await expect.poll(() => find('#age').textContent()).toBe('30');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "catch-all * matches every event",
    "html": "\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource CatchAllSSE from \"/sse\"\n                    on \"*\" as string\n                        put (event.type + \",\" + #out's innerHTML) into #out\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'a', data: 'x' },\n            { event: 'b', data: 'x', delay: 30 },\n            { event: 'c', data: 'x', delay: 30 },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource CatchAllSSE from \"/sse\"\n                    on \"*\" as string\n                        put (event.type + \",\" + #out's innerHTML) into #out\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#out').textContent()).toContain('c,');\n        var text = await find('#out').textContent();\n        // Should contain all three named events (and possibly open)\n        expect(text).toContain('a,');\n        expect(text).toContain('b,');\n        expect(text).toContain('c,');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "close() stops the connection",
    "html": "\n            <div id=\"out\">0</div>\n            <script type=\"text/hyperscript\">\n                eventsource CloseSSE from \"/sse\"\n                    on \"message\" as string\n                        put it into #out\n                        if it is \"1\"\n                            call CloseSSE.close()\n                        end\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { data: '1' },\n            { data: '2', delay: 500 },\n            { data: '3', delay: 500 },\n        ], { keepOpen: true });\n        await html(`\n            <div id=\"out\">0</div>\n            <script type=\"text/hyperscript\">\n                eventsource CloseSSE from \"/sse\"\n                    on \"message\" as string\n                        put it into #out\n                        if it is \"1\"\n                            call CloseSSE.close()\n                        end\n                    end\n                end\n            </script>\n        `);\n        // Should get \"1\" then stop\n        await expect.poll(() => find('#out').textContent()).toBe('1');\n        // Wait long enough that message \"2\" would have arrived if not closed\n        await evaluate(() => new Promise(r => setTimeout(r, 800)));\n        expect(await find('#out').textContent()).toBe('1');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "dispatches named SSE events on the element",
    "html": "\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /stream-named as Stream\n                          on status from me\n                            put event.detail.data into #out\n                          end\">go</button>\n        ",
    "body": "\n        await mockSSE(page, '/stream-named', [\n            { event: 'status', data: 'loading', delay: 10 },\n            { event: 'status', data: 'done', delay: 10 },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /stream-named as Stream\n                          on status from me\n                            put event.detail.data into #out\n                          end\">go</button>\n        `);\n        await find('button').click();\n        await expect.poll(() => find('#out').textContent()).toBe('done');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/eventsource",
    "name": "dynamic open with new URL",
    "html": "\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource DynSSE\n                    on \"msg\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse-a', [{ event: 'msg', data: 'from-a' }]);\n        await html(`\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource DynSSE\n                    on \"msg\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        `);\n        // Not connected yet - open manually\n        await evaluate(() => DynSSE.open('/sse-a'));\n        await expect.poll(() => find('#out').textContent()).toBe('from-a');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "fires streamEnd when the stream closes",
    "html": "\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /streamEnd as Stream\n                          wait for streamEnd from me\n                          put 'finished' into #out\">go</button>\n        ",
    "body": "\n        await mockSSE(page, '/streamEnd', [\n            { data: 'msg', delay: 50 },\n            { data: 'msg2', delay: 50 },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /streamEnd as Stream\n                          wait for streamEnd from me\n                          put 'finished' into #out\">go</button>\n        `);\n        await find('button').click();\n        await expect.poll(() => find('#out').textContent()).toBe('finished');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/eventsource",
    "name": "iterates plain messages with for loop",
    "html": "\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /stream-iter as Stream\n                          for message in the result\n                            put message + ' ' at end of #out\n                          end\">go</button>\n        ",
    "body": "\n        await mockSSE(page, '/stream-iter', [\n            { data: 'one', delay: 10 },\n            { data: 'two', delay: 10 },\n            { data: 'three', delay: 10 },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <button _=\"on click\n                          fetch /stream-iter as Stream\n                          for message in the result\n                            put message + ' ' at end of #out\n                          end\">go</button>\n        `);\n        await find('button').click();\n        await expect.poll(() => find('#out').textContent()).toBe('one two three ');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/eventsource",
    "name": "open and close lifecycle events fire",
    "html": "\n            <div id=\"opened\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource LifecycleSSE from \"/sse\"\n                    on \"open\"\n                        put \"yes\" into #opened\n                    end\n                    on \"message\" as string\n                        call LifecycleSSE.close()\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { data: 'hi' },\n        ]);\n        await html(`\n            <div id=\"opened\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource LifecycleSSE from \"/sse\"\n                    on \"open\"\n                        put \"yes\" into #opened\n                    end\n                    on \"message\" as string\n                        call LifecycleSSE.close()\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#opened').textContent()).toBe('yes');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "receives named events",
    "html": "\n            <div id=\"greet\"></div>\n            <div id=\"bye\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource NamedSSE from \"/sse\"\n                    on \"greeting\" as json\n                        put it.msg into #greet\n                    end\n                    on \"farewell\" as json\n                        put it.msg into #bye\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'greeting', data: '{\"msg\":\"hi\"}' },\n            { event: 'farewell', data: '{\"msg\":\"bye\"}', delay: 50 },\n        ]);\n        await html(`\n            <div id=\"greet\"></div>\n            <div id=\"bye\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource NamedSSE from \"/sse\"\n                    on \"greeting\" as json\n                        put it.msg into #greet\n                    end\n                    on \"farewell\" as json\n                        put it.msg into #bye\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#greet').textContent()).toBe('hi');\n        await expect.poll(() => find('#bye').textContent()).toBe('bye');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "receives unnamed messages",
    "html": "\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource TestSSE from \"/sse\"\n                    on \"message\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { data: 'hello' },\n            { data: 'world', delay: 50 },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource TestSSE from \"/sse\"\n                    on \"message\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#out').textContent()).toBe('world');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "wildcard pattern matches multiple events",
    "html": "\n            <div id=\"count\">0</div>\n            <script type=\"text/hyperscript\">\n                eventsource WildSSE from \"/sse\"\n                    on \"user.*\" as json\n                        set c to #count's innerHTML as Int\n                        put (c + 1) into #count\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'user.login', data: '{\"n\":1}' },\n            { event: 'user.logout', data: '{\"n\":2}', delay: 30 },\n            { event: 'system.ping', data: '{\"n\":3}', delay: 30 },\n            { event: 'user.update', data: '{\"n\":4}', delay: 30 },\n        ]);\n        await html(`\n            <div id=\"count\">0</div>\n            <script type=\"text/hyperscript\">\n                eventsource WildSSE from \"/sse\"\n                    on \"user.*\" as json\n                        set c to #count's innerHTML as Int\n                        put (c + 1) into #count\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#count').textContent()).toBe('3');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "with headers sends custom headers",
    "html": "\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource HeaderSSE from \"/sse\" with headers {\"X-Custom\": \"test123\"}\n                    on \"ack\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'ack', data: 'ok' },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource HeaderSSE from \"/sse\" with headers {\"X-Custom\": \"test123\"}\n                    on \"ack\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#out').textContent()).toBe('ok');\n        var req = await evaluate(() => window.__lastSSERequest);\n        expect(req.headers['X-Custom']).toBe('test123');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/eventsource",
    "name": "with method sends POST",
    "html": "\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource PostSSE from \"/sse\" with method \"POST\"\n                    on \"ack\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        ",
    "body": "\n        await mockSSE(page, '/sse', [\n            { event: 'ack', data: 'ok' },\n        ]);\n        await html(`\n            <div id=\"out\"></div>\n            <script type=\"text/hyperscript\">\n                eventsource PostSSE from \"/sse\" with method \"POST\"\n                    on \"ack\" as string\n                        put it into #out\n                    end\n                end\n            </script>\n        `);\n        await expect.poll(() => find('#out').textContent()).toBe('ok');\n        var req = await evaluate(() => window.__lastSSERequest);\n        expect(req.method).toBe('POST');\n        await restoreFetch(page);\n    ",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "ext/hs-include",
    "name": "JSON-serializes object values",
    "html": "\n            <button _=\"init set :obj to {name: 'Alice', age: 30}\"\n                    hs-include=\":obj\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button _=\"init set :obj to {name: 'Alice', age: 30}\"\n                    hs-include=\":obj\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = await getBody()\n        expect(body).toContain('obj=')\n        var params = new URLSearchParams(body)\n        expect(JSON.parse(params.get('obj'))).toEqual({ name: 'Alice', age: 30 })\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "direct hs-include takes precedence over inherited",
    "html": "\n            <div _=\"init set :parent to 1\" hs-include:inherited=\":parent\">\n                <button _=\"init set :child to 2\"\n                        hs-include=\":child\"\n                        hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <div _=\"init set :parent to 1\" hs-include:inherited=\":parent\">\n                <button _=\"init set :child to 2\"\n                        hs-include=\":child\"\n                        hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = await getBody()\n        expect(body).toContain('child=2')\n        expect(body).not.toContain('parent')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "elements without hs-include are unaffected",
    "html": "\n            <button _=\"init set :secret to 42\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button _=\"init set :secret to 42\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = (await getBody()) || ''\n        expect(body).not.toContain('secret')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "hs-include:inherited applies to descendant triggers",
    "html": "\n            <div _=\"init set :ctx to 'parent-val'\" hs-include:inherited=\":ctx\">\n                <button hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <div _=\"init set :ctx to 'parent-val'\" hs-include:inherited=\":ctx\">\n                <button hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        expect(await getBody()).toContain('ctx=parent-val')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "includes a named element-scoped variable",
    "html": "\n            <button _=\"init set :userId to 42\"\n                    hs-include=\":userId\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button _=\"init set :userId to 42\"\n                    hs-include=\":userId\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        expect(await getBody()).toContain('userId=42')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "includes multiple named variables",
    "html": "\n            <button _=\"init set :a to 1 then set :b to 2\"\n                    hs-include=\":a, :b\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button _=\"init set :a to 1 then set :b to 2\"\n                    hs-include=\":a, :b\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = await getBody()\n        expect(body).toContain('a=1')\n        expect(body).toContain('b=2')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "missing variables are silently skipped",
    "html": "\n            <button hs-include=\":nonexistent\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button hs-include=\":nonexistent\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = (await getBody()) || ''\n        expect(body).not.toContain('nonexistent')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "resolves inherited var via ^",
    "html": "\n            <div _=\"init set :shared to 99\">\n                <button hs-include=\"^shared\"\n                        hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <div _=\"init set :shared to 99\">\n                <button hs-include=\"^shared\"\n                        hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            </div>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        expect(await getBody()).toContain('shared=99')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "resolves var from another element via #selector",
    "html": "\n            <div id=\"source\" _=\"init set :data to 'hello'\"></div>\n            <button hs-include=\"#source:data\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <div id=\"source\" _=\"init set :data to 'hello'\"></div>\n            <button hs-include=\"#source:data\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        expect(await getBody()).toContain('data=hello')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/hs-include",
    "name": "wildcard includes all element-scoped vars",
    "html": "\n            <button _=\"init set :x to 10 then set :y to 20\"\n                    hs-include=\"*\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        ",
    "body": "\n        var getBody = await capture('POST', '/api')\n        await html(`\n            <button _=\"init set :x to 10 then set :y to 20\"\n                    hs-include=\"*\"\n                    hx-post=\"/api\" hx-target=\"#out\">Send</button>\n            <div id=\"out\"></div>\n        `)\n        await find('button').click()\n        await expect.poll(() => find('#out span').textContent()).toBe('ok')\n        var body = await getBody()\n        expect(body).toContain('x=10')\n        expect(body).toContain('y=20')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss hidden class",
    "html": "<div _='on click hide with twDisplay'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide with twDisplay'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/hidden/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/hidden/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss hidden class default strategy",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twDisplay\")\n\t\tawait html(\"<div _='on click hide'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/hidden/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/hidden/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss invisible class",
    "html": "<div _='on click hide with twVisibility'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide with twVisibility'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/invisible/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/invisible/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss invisible class default strategy",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twVisibility\")\n\t\tawait html(\"<div _='on click hide'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/invisible/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/invisible/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss opacity-0 class",
    "html": "<div _='on click hide with twOpacity'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide with twOpacity'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/opacity-0/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/opacity-0/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can hide element, with tailwindcss opacity-0 class default strategy",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twOpacity\")\n\t\tawait html(\"<div _='on click hide'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/opacity-0/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveClass(/opacity-0/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing hidden class",
    "html": "<div class='hidden' _='on click show with twDisplay'></div>",
    "body": "\n\t\tawait html(\"<div class='hidden' _='on click show with twDisplay'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/hidden/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/hidden/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing hidden class default strategy",
    "html": "<div class='hidden' _='on click show'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twDisplay\")\n\t\tawait html(\"<div class='hidden' _='on click show'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/hidden/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/hidden/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing invisible class",
    "html": "<div class='invisible' _='on click show with twVisibility'></div>",
    "body": "\n\t\tawait html(\"<div class='invisible' _='on click show with twVisibility'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/invisible/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/invisible/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing invisible class default strategy",
    "html": "<div class='invisible' _='on click show'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twVisibility\")\n\t\tawait html(\"<div class='invisible' _='on click show'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/invisible/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/invisible/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing opacity-0 class",
    "html": "<div class='opacity-0' _='on click show with twOpacity'></div>",
    "body": "\n\t\tawait html(\"<div class='opacity-0' _='on click show with twOpacity'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/opacity-0/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/opacity-0/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "ext/tailwind",
    "name": "can show element, with tailwindcss removing opacity-0 class default strategy",
    "html": "<div class='opacity-0' _='on click show'></div>",
    "body": "\n\t\tawait evaluate(() => _hyperscript.config.defaultHideShowStrategy = \"twOpacity\")\n\t\tawait html(\"<div class='opacity-0' _='on click show'></div>\")\n\t\tawait expect(find('div')).toHaveClass(/opacity-0/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/opacity-0/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "Response can be converted to JSON via as JSON",
    "html": "<div _=\"on click fetch /test as Response then put (it as JSON).name into me\"></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.fulfill({\n\t\t\t\tstatus: 200,\n\t\t\t\tcontentType: 'application/json',\n\t\t\t\tbody: JSON.stringify({ name: \"Joe\" })\n\t\t\t});\n\t\t});\n\t\tawait html(\"<div _=\\\"on click fetch /test as Response then put (it as JSON).name into me\\\"></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"Joe\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "allows the event handler to change the fetch parameters",
    "html": "<div _='on click fetch \"/test\" then put it into my.innerHTML end'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.addEventListener('hyperscript:beforeFetch', (event) => {\n\t\t\t\tevent.detail.headers = {'X-CustomHeader': 'foo'};\n\t\t\t});\n\t\t});\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tconst headers = route.request().headers();\n\t\t\tif (headers['x-customheader'] === 'foo') {\n\t\t\t\tawait route.fulfill({status: 200, contentType: 'text/html', body: 'yay'});\n\t\t\t} else {\n\t\t\t\tawait route.fulfill({status: 200, contentType: 'text/html', body: 'no header'});\n\t\t\t}\n\t\t});\n\t\tawait html(\"<div _='on click fetch \\\"/test\\\" then put it into my.innerHTML end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "as response does not throw on 404",
    "html": "<div _='on click fetch /test as response then put it.status into me'></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.fulfill({ status: 404, body: 'not found' });\n\t\t});\n\t\tawait html(\"<div _='on click fetch /test as response then put it.status into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"404\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can catch an error that occurs when using fetch",
    "html": "<div _='on click fetch /test catch e log e put \"yay\" into me'></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.abort();\n\t\t});\n\t\tawait html(\"<div _='on click fetch /test catch e log e put \\\"yay\\\" into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch",
    "html": "<div _='on click fetch \"/test\" then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch \\\"/test\\\" then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ a custom conversion",
    "html": "<div _='on click fetch /test as Number then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '1.2000', {status: 200, contentType: 'text/plain'});\n\t\tawait html(\"<div _='on click fetch /test as Number then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1.2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ a naked URL",
    "html": "<div _='on click fetch /test then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ html",
    "html": "<div _='on click fetch /test as html then log it then put it into my.innerHTML put its childElementCount into my @data-count'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '<br>', {status: 200, contentType: 'text/html'});\n\t\tawait html(\n\t\t\t\"<div _='on click fetch /test as html then log it then put it into my.innerHTML put its childElementCount into my @data-count'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"[object DocumentFragment]\");\n\t\tawait expect(find('div')).toHaveAttribute('data-count', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ json",
    "html": "<div _='on click fetch /test as json then get result as JSONString then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '{\"foo\":1}', {status: 200, contentType: 'application/json'});\n\t\tawait html(\n\t\t\t\"<div _='on click fetch /test as json then get result as JSONString then put it into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText('{\"foo\":1}');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ json using JSON syntax",
    "html": "<div _='on click fetch /test as JSON then get result as JSONString then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '{\"foo\":1}', {status: 200, contentType: 'application/json'});\n\t\tawait html(\n\t\t\t\"<div _='on click fetch /test as JSON then get result as JSONString then put it into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText('{\"foo\":1}');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ json using Object syntax",
    "html": "<div _='on click fetch /test as Object then get result as JSONString then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '{\"foo\":1}', {status: 200, contentType: 'application/json'});\n\t\tawait html(\n\t\t\t\"<div _='on click fetch /test as Object then get result as JSONString then put it into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText('{\"foo\":1}');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch w/ json using Object syntax and an 'an' prefix",
    "html": "<div _='on click fetch /test as an Object then get result as JSONString then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '{\"foo\":1}', {status: 200, contentType: 'application/json'});\n\t\tawait html(\n\t\t\t\"<div _='on click fetch /test as an Object then get result as JSONString then put it into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText('{\"foo\":1}');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple fetch with a response object",
    "html": "<div _='on click fetch /test as response then if its.ok put \"yep\" into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('GET', '/test', '{\"foo\":1}', {status: 200, contentType: 'application/json'});\n\t\tawait html(\"<div _='on click fetch /test as response then if its.ok put \\\"yep\\\" into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yep\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple post",
    "html": "<div _='on click fetch /test {method:\"POST\"} then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('POST', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test {method:\\\"POST\\\"} then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple post alt syntax w/ curlies",
    "html": "<div _='on click fetch /test with {method:\"POST\"} then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('POST', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test with {method:\\\"POST\\\"} then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can do a simple post alt syntax without curlies",
    "html": "<div _='on click fetch /test with method:\"POST\" then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('POST', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test with method:\\\"POST\\\" then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can put response conversion after with",
    "html": "<div _='on click fetch /test with {method:\"POST\"} as text then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('POST', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test with {method:\\\"POST\\\"} as text then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "can put response conversion before with",
    "html": "<div _='on click fetch /test as text with {method:\"POST\"} then put it into my.innerHTML'></div>",
    "body": "\n\t\tawait mock('POST', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait html(\"<div _='on click fetch /test as text with {method:\\\"POST\\\"} then put it into my.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "do not throw passes through 404 response",
    "html": "<div _='on click fetch /test do not throw then put it into me'></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.fulfill({ status: 404, body: 'the body' });\n\t\t});\n\t\tawait html(\"<div _='on click fetch /test do not throw then put it into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"the body\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "don't throw passes through 404 response",
    "html": "<div _=\"on click fetch /test don't throw then put it into me\"></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.fulfill({ status: 404, body: 'the body' });\n\t\t});\n\t\tawait html('<div _=\"on click fetch /test don\\'t throw then put it into me\"></div>');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"the body\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "submits the fetch parameters to the event handler",
    "html": "<div _='on click fetch \"/test\" {headers: {\"X-CustomHeader\": \"foo\"}} then put it into my.innerHTML end'></div>",
    "body": "\n\t\tawait mock('GET', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait evaluate(() => {\n\t\t\twindow.headerCheckPassed = false;\n\t\t\twindow.addEventListener('hyperscript:beforeFetch', (event) => {\n\t\t\t\tif (event.detail.headers && event.detail.headers['X-CustomHeader'] === 'foo') {\n\t\t\t\t\twindow.headerCheckPassed = true;\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tawait html(\"<div _='on click fetch \\\"/test\\\" {headers: {\\\"X-CustomHeader\\\": \\\"foo\\\"}} then put it into my.innerHTML end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t\texpect(await evaluate(() => window.headerCheckPassed)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "throws on non-2xx response by default",
    "html": "<div _='on click fetch /test catch e put \"caught\" into me'></div>",
    "body": "\n\t\tawait page.route('**/test', async (route) => {\n\t\t\tawait route.fulfill({ status: 404, body: 'not found' });\n\t\t});\n\t\tawait html(\"<div _='on click fetch /test catch e put \\\"caught\\\" into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"caught\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "fetch",
    "name": "triggers an event just before fetching",
    "html": "<div _='on click fetch \"/test\" then put it into my.innerHTML end'></div>",
    "body": "\n\t\tawait mock('GET', '/test', 'yay', {status: 200, contentType: 'text/html'});\n\t\tawait evaluate(() => {\n\t\t\twindow.addEventListener('hyperscript:beforeFetch', (event) => {\n\t\t\t\tevent.target.className = \"foo-set\";\n\t\t\t});\n\t\t});\n\t\tawait html(\"<div _='on click fetch \\\"/test\\\" then put it into my.innerHTML end'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo-set/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo-set/);\n\t\tawait expect(find('div')).toHaveText(\"yay\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "focus",
    "name": "can blur an element",
    "html": "<input id='i1' _='on focus wait 10ms then blur me' />",
    "body": "\n\t\tawait html(\"<input id='i1' _='on focus wait 10ms then blur me' />\");\n\t\tawait find('#i1').focus();\n\t\tawait find('#i1').evaluate(el => new Promise(r => setTimeout(r, 50)));\n\t\tvar focused = await evaluate(() => document.activeElement.tagName);\n\t\texpect(focused).toBe(\"BODY\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "focus",
    "name": "can focus an element",
    "html": "<input id='i1' /><button _='on click focus #i1'></button>",
    "body": "\n\t\tawait html(\"<input id='i1' /><button _='on click focus #i1'></button>\");\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar focused = await evaluate(() => document.activeElement.id);\n\t\texpect(focused).toBe(\"i1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "focus",
    "name": "focus with no target focuses me",
    "html": "<input id='i1' _='on click focus' />",
    "body": "\n\t\tawait html(\"<input id='i1' _='on click focus' />\");\n\t\tawait find('#i1').dispatchEvent('click');\n\t\tvar focused = await evaluate(() => document.activeElement.id);\n\t\texpect(focused).toBe(\"i1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "go",
    "name": "can parse go to with string URL",
    "html": "<div _='on click go to \"#test-hash\"'></div>",
    "body": "\n\t\t// verify parsing succeeds - go to with a quoted string\n\t\tawait html(\"<div _='on click go to \\\"#test-hash\\\"'></div>\");\n\t\t// no error on parse = success\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "go",
    "name": "deprecated scroll form still works",
    "html": "<div style='height: 2000px'></div><div id='target'>Target</div><div _='on click go to the top of #target'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 2000px'></div>\" +\n\t\t\t\"<div id='target'>Target</div>\" +\n\t\t\t\"<div _='on click go to the top of #target'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(3)').dispatchEvent('click');\n\t\tvar inView = await evaluate(() => {\n\t\t\tvar rect = document.querySelector('#target').getBoundingClientRect();\n\t\t\treturn rect.top >= 0 && rect.top < window.innerHeight;\n\t\t});\n\t\texpect(inView).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "go",
    "name": "deprecated url keyword still parses",
    "html": "<div _='on click go to url /test'></div>",
    "body": "\n\t\tawait html(\"<div _='on click go to url /test'></div>\");\n\t\t// no error on parse = success\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "go",
    "name": "go to element scrolls",
    "html": "<div style='height: 2000px'></div><div id='target'>Target</div><div _='on click go to #target'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 2000px'></div>\" +\n\t\t\t\"<div id='target'>Target</div>\" +\n\t\t\t\"<div _='on click go to #target'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(3)').dispatchEvent('click');\n\t\tvar inView = await evaluate(() => {\n\t\t\tvar rect = document.querySelector('#target').getBoundingClientRect();\n\t\t\treturn rect.top >= 0 && rect.top < window.innerHeight;\n\t\t});\n\t\texpect(inView).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "go",
    "name": "go to naked URL starting with / parses",
    "html": "<div _='on click go to /test/path'></div>",
    "body": "\n\t\tawait html(\"<div _='on click go to /test/path'></div>\");\n\t\t// no error on parse = success\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt bubbling only stops propagation, not default",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <div id='inner' _='on click halt bubbling then add .continued'>click me</div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <div id='inner' _='on click halt bubbling then add .continued'>click me</div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#inner').dispatchEvent('click');\n\t\t// bubbling stopped - outer should NOT get click\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/);\n\t\t// but execution halts (no keepExecuting)\n\t\tawait expect(find('#inner')).not.toHaveClass(/continued/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt default only prevents default, not propagation",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <div id='inner' _='on click halt default'>click me</div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <div id='inner' _='on click halt default'>click me</div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#inner').dispatchEvent('click');\n\t\t// default halted but propagation NOT stopped - outer SHOULD get click\n\t\tawait expect(find('#outer')).toHaveClass(/outer-clicked/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt stops execution after the halt",
    "html": "<div _='on click halt then add .should-not-happen'>test</div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click halt then add .should-not-happen'>test</div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/should-not-happen/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt the event stops propagation but continues execution",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <div id='inner' _='on click halt the event then add .continued'>click me</div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <div id='inner' _='on click halt the event then add .continued'>click me</div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#inner').dispatchEvent('click');\n\t\t// halt the event stops propagation but continues execution\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/);\n\t\tawait expect(find('#inner')).toHaveClass(/continued/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt the event's stops propagation but continues execution",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <div id='inner' _=\"on click halt the event's then add .continued\">click me</div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <div id='inner' _=\\\"on click halt the event's then add .continued\\\">click me</div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#inner').dispatchEvent('click');\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/);\n\t\tawait expect(find('#inner')).toHaveClass(/continued/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "halt",
    "name": "halt works outside of event context",
    "html": "",
    "body": "\n\t\t// halt in init (no event) should not throw \"did not return a next element\"\n\t\tvar error = await evaluate(() => {\n\t\t\treturn new Promise(resolve => {\n\t\t\t\twindow.addEventListener('error', function handler(e) {\n\t\t\t\t\twindow.removeEventListener('error', handler);\n\t\t\t\t\tresolve(e.message);\n\t\t\t\t});\n\t\t\t\tvar wa = document.getElementById('work-area');\n\t\t\t\twa.innerHTML = \"<div _='init halt'></div>\";\n\t\t\t\t_hyperscript.processNode(wa);\n\t\t\t\tsetTimeout(() => resolve(null), 200);\n\t\t\t});\n\t\t});\n\t\texpect(error).toBeNull();\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "halt",
    "name": "halts event propagation and default",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <a id='inner' href='#shouldnot' _='on click halt'>click me</a></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <a id='inner' href='#shouldnot' _='on click halt'>click me</a>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#inner').dispatchEvent('click');\n\t\t// halt stops propagation - outer should NOT get the click\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can configure hidden as the default hide strategy",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait evaluate(() => { _hyperscript.config.defaultHideShowStrategy = \"hidden\" });\n\t\tawait html(\"<div _='on click hide'></div>\");\n\t\tawait expect(find('div')).not.toHaveAttribute('hidden', '');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('hidden', '');\n\t\tawait evaluate(() => { delete _hyperscript.config.defaultHideShowStrategy });\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can filter hide via the when clause",
    "html": "<div id='trigger' _='on click hide <div/> in me when it matches .hideable'>  <div id='d1' class='hideable'></div>  <div id='d2'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click hide <div/> in me when it matches .hideable'>\" +\n\t\t\t\"  <div id='d1' class='hideable'></div>\" +\n\t\t\t\"  <div id='d2'></div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveCSS('display', 'none');\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with display:none explicitly",
    "html": "<div _='on click hide me with display'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me with display'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with no target",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toBeHidden();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with no target followed by command",
    "html": "<div _='on click hide add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide add .foo'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with no target followed by then",
    "html": "<div _='on click hide then add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide then add .foo'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with no target with a with",
    "html": "<div _='on click hide with display then add .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide with display then add .foo'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with opacity style literal",
    "html": "<div _='on click hide me with *opacity'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me with *opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element with opacity:0",
    "html": "<div _='on click hide me with opacity'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me with opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element, with display:none by default",
    "html": "<div _='on click hide me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide element, with visibility:hidden",
    "html": "<div _='on click hide me with visibility'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me with visibility'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'hidden');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide other elements",
    "html": "<div class='hideme'></div><div _='on click hide .hideme'></div>",
    "body": "\n\t\tawait html(\"<div class='hideme'></div><div _='on click hide .hideme'></div>\");\n\t\tawait expect(find('.hideme')).toHaveCSS('display', 'block');\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('.hideme')).toHaveCSS('display', 'none');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide via the hidden attribute strategy",
    "html": "<div _='on click hide me with hidden'></div>",
    "body": "\n\t\tawait html(\"<div _='on click hide me with hidden'></div>\");\n\t\tawait expect(find('div')).not.toHaveAttribute('hidden', '');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('hidden', '');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can hide with custom strategy",
    "html": "<div _='on click hide with myHide'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\t_hyperscript.config.hideShowStrategies = {\n\t\t\t\tmyHide: function (op, element, arg) {\n\t\t\t\t\tif (op == \"hide\") {\n\t\t\t\t\t\telement.classList.add(\"foo\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\telement.classList.remove(\"foo\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t\tawait html(\"<div _='on click hide with myHide'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "can set default to custom strategy",
    "html": "<div _='on click hide'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\t_hyperscript.config.hideShowStrategies = {\n\t\t\t\tmyHide: function (op, element, arg) {\n\t\t\t\t\tif (op == \"hide\") {\n\t\t\t\t\t\telement.classList.add(\"foo\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\telement.classList.remove(\"foo\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t\t_hyperscript.config.defaultHideShowStrategy = \"myHide\";\n\t\t});\n\t\tawait html(\"<div _='on click hide'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "hide",
    "name": "hide element then show element retains original display",
    "html": "<div _='on click 1 hide                          on click 2 show'></div>",
    "body": "\n\t\tawait html(\"<div _='on click 1 hide\" +\n\t\t\t\"                          on click 2 show'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic else branch works",
    "html": "<div _='on click if false else put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false else put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic else branch works with end",
    "html": "<div _='on click if false else put \"foo\" into me.innerHTML end'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false else put \\\"foo\\\" into me.innerHTML end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic else branch works with multiple commands",
    "html": "<div _='on click if false put \"bar\" into me.innerHTML                                  else log me then                                       put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\\'on click if false put \"bar\" into me.innerHTML' +\n\t\t\t\t\"                                  else log me then\" +\n\t\t\t\t'                                       put \"foo\" into me.innerHTML\\'></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic else if branch works",
    "html": "<div _='on click if false else if true put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false else if true put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic else if branch works with end",
    "html": "<div _='on click if false                                   else if true put \"foo\" into me.innerHTML end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click if false \" +\n\t\t\t\t'                                  else if true put \"foo\" into me.innerHTML end\\'></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic true branch works",
    "html": "<div _='on click if true put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if true put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic true branch works with end",
    "html": "<div _='on click if true put \"foo\" into me.innerHTML end'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if true put \\\"foo\\\" into me.innerHTML end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic true branch works with multiple commands",
    "html": "<div _='on click if true log me then                                   put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click if true log me then \" +\n\t\t\t\t'                                  put \"foo\" into me.innerHTML\\'></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic true branch works with naked else",
    "html": "<div _='on click if true put \"foo\" into me.innerHTML else'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if true put \\\"foo\\\" into me.innerHTML else'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "basic true branch works with naked else end",
    "html": "<div _='on click if true put \"foo\" into me.innerHTML else end'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if true put \\\"foo\\\" into me.innerHTML else end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "false branch with a wait works",
    "html": "<div _='on click if false else wait 10 ms then put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false else wait 10 ms then put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "if on new line does not join w/ else",
    "html": "<div _='on click \n                      if window.tmp then\n                      else\n                        if window.tmp then                        end\n                        put \"foo\" into me\n                      end\n                  '</div>",
    "body": "\n\t\tawait evaluate(() => window.tmp = false);\n\t\tawait html(\"<div _='on click \\n\" +\n\t\t\t\"                      if window.tmp then\\n\" +\n\t\t\t\"                      else\\n\" +\n\t\t\t\"                        if window.tmp then\" +\n\t\t\t\"                        end\\n\" +\n\t\t\t\"                        put \\\"foo\\\" into me\\n\" +\n\t\t\t\"                      end\\n\" +\n\t\t\t\"                  '\" +\n\t\t\t\"</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\n\t\tawait evaluate(() => window.tmp = true);\n\t\tawait evaluate(() => document.querySelector('#work-area div').innerHTML = \"\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "if properly passes execution along if child is not executed",
    "html": "<div _='on click if false end put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false end put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "if properly supports nested if statements and end block",
    "html": "<div _='on click \n                      if window.tmp then\n                        put \"foo\" into me\n                      else if not window.tmp then\n                        // do nothing\n                      end\n                  catch e\n                      // just here for the parsing...\n'</div>",
    "body": "\n\t\tawait evaluate(() => window.tmp = false);\n\t\tawait html(\"<div _='on click \\n\" +\n\t\t\t\"                      if window.tmp then\\n\" +\n\t\t\t\"                        put \\\"foo\\\" into me\\n\" +\n\t\t\t\"                      else if not window.tmp then\\n\" +\n\t\t\t\"                        // do nothing\\n\" +\n\t\t\t\"                      end\\n\" +\n\t\t\t\"                  catch e\\n\" +\n\t\t\t\"                      // just here for the parsing...\\n'\" +\n\t\t\t\"</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"\");\n\n\t\tawait evaluate(() => window.tmp = true);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "otherwise alias works",
    "html": "<div _='on click if false otherwise put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false otherwise put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "passes the sieve test",
    "html": "",
    "body": "\n\t\tvar str =\n\t\t\t\"if x is less than 10\\n\" +\n\t\t\t\"  if x is less than 3\\n\" +\n\t\t\t\"    if x is less than 2\\n\" +\n\t\t\t\"      return 1\\n\" +\n\t\t\t\"    else\\n\" +\n\t\t\t\"      return 2\\n\" +\n\t\t\t\"    end\\n\" +\n\t\t\t\"  else if x is less than 4\\n\" +\n\t\t\t\"      return 3\\n\" +\n\t\t\t\"  else if x is 4\\n\" +\n\t\t\t\"    return 4\\n\" +\n\t\t\t\"  else\\n\" +\n\t\t\t\"    if x is 5\\n\" +\n\t\t\t\"       return 5\\n\" +\n\t\t\t\"    else\\n\" +\n\t\t\t\"       return 6\\n\" +\n\t\t\t\"    end\\n\" +\n\t\t\t\"  end\\n\" +\n\t\t\t\"else\\n\" +\n\t\t\t\"  return 10\\n\" +\n\t\t\t\"end\";\n\n\t\texpect(await run(str, {locals: {x: 1}})).toBe(1);\n\t\texpect(await run(str, {locals: {x: 2}})).toBe(2);\n\t\texpect(await run(str, {locals: {x: 3}})).toBe(3);\n\t\texpect(await run(str, {locals: {x: 4}})).toBe(4);\n\t\texpect(await run(str, {locals: {x: 5}})).toBe(5);\n\t\texpect(await run(str, {locals: {x: 6}})).toBe(6);\n\t\texpect(await run(str, {locals: {x: 7}})).toBe(6);\n\t\texpect(await run(str, {locals: {x: 8}})).toBe(6);\n\t\texpect(await run(str, {locals: {x: 9}})).toBe(6);\n\t\texpect(await run(str, {locals: {x: 10}})).toBe(10);\n\t\texpect(await run(str, {locals: {x: 11}})).toBe(10);\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "if",
    "name": "triple else if branch works",
    "html": "<div _='on click if false else if false else put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if false else if false else put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "triple else if branch works with end",
    "html": "<div _='on click if false                                   else if false                                  else put \"foo\" into me.innerHTML end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click if false \" +\n\t\t\t\t\"                                  else if false\" +\n\t\t\t\t'                                  else put \"foo\" into me.innerHTML end\\'></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "if",
    "name": "true branch with a wait works",
    "html": "<div _='on click if true wait 10 ms then put \"foo\" into me.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click if true wait 10 ms then put \\\"foo\\\" into me.innerHTML'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement a property",
    "html": "<div _=\"on click decrement my.innerHTML\">3</div>",
    "body": "\n\t\tawait html(`<div _=\"on click decrement my.innerHTML\">3</div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement a value multiple times",
    "html": "<div _=\"on click decrement my.innerHTML\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click decrement my.innerHTML\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"-5\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement a variable",
    "html": "<div _=\"on click set value to 20 then decrement value by 2 then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set value to 20 then decrement value by 2 then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"18\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement an array element",
    "html": "<div _=\"on click set arr to [10, 20, 30] then decrement arr[1] then put arr[1] into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set arr to [10, 20, 30] then decrement arr[1] then put arr[1] into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"19\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement an attribute",
    "html": "<div value=\"5\" _=\"on click decrement @value then put @value into me\"></div>",
    "body": "\n\t\tawait html(`<div value=\"5\" _=\"on click decrement @value then put @value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement an empty variable",
    "html": "<div _=\"on click decrement value then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click decrement value then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"-1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement an floating point numbers",
    "html": "<div value=\"5\" _=\"on click set value to 6.1 then decrement value by 5.1 then put value into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div value=\"5\" _=\"on click set value to 6.1 then decrement value by 5.1 then put value into me\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can decrement by zero",
    "html": "<div _=\"on click set value to 20 then decrement value by 0 then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set value to 20 then decrement value by 0 then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"20\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a possessive property",
    "html": "<div id=\"d1\" _=\"on click increment #d1's innerHTML\">5</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click increment #d1's innerHTML\">5</div>`\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"6\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a property",
    "html": "<div _=\"on click increment my.innerHTML\">3</div>",
    "body": "\n\t\tawait html(`<div _=\"on click increment my.innerHTML\">3</div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"6\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a property of expression",
    "html": "<div id=\"d1\" _=\"on click increment innerHTML of #d1\">5</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"on click increment innerHTML of #d1\">5</div>`\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"6\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a style ref",
    "html": "<div _=\"on click set my *opacity to 0.5 then increment *opacity by 0.25 then put *opacity into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click set my *opacity to 0.5 then increment *opacity by 0.25 then put *opacity into me\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"0.75\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a value multiple times",
    "html": "<div _=\"on click increment my.innerHTML\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click increment my.innerHTML\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment a variable",
    "html": "<div _=\"on click set value to 20 then increment value by 2 then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set value to 20 then increment value by 2 then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"22\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment an array element",
    "html": "<div _=\"on click set arr to [10, 20, 30] then increment arr[1] then put arr[1] into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set arr to [10, 20, 30] then increment arr[1] then put arr[1] into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"21\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment an attribute",
    "html": "<div value=\"5\" _=\"on click increment @value then put @value into me\"></div>",
    "body": "\n\t\tawait html(`<div value=\"5\" _=\"on click increment @value then put @value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"8\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment an empty variable",
    "html": "<div _=\"on click increment value then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click increment value then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment an floating point numbers",
    "html": "<div value=\"5\" _=\"on click set value to 5.2 then increment value by 6.1 then put value into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div value=\"5\" _=\"on click set value to 5.2 then increment value by 6.1 then put value into me\"></div>`\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"11.3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment by zero",
    "html": "<div _=\"on click set value to 20 then increment value by 0 then put value into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click set value to 20 then increment value by 0 then put value into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"20\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "increment",
    "name": "can increment refer to result",
    "html": "<div _=\"on click increment value by 2 then put it into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click increment value by 2 then put it into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "init",
    "name": "can define an init block in a script",
    "html": "<script type='text/hyperscript'>  init    set window.foo to 42  end</script>",
    "body": "\n\t\tawait html(\"<script type='text/hyperscript'>  init    set window.foo to 42  end</script>\")\n\t\tawait expect.poll(() => evaluate(() => window.foo)).toBe(42)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "init",
    "name": "can define an init block inline",
    "html": "<div _='init                  set my.foo to 42                end               on click put my.foo into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='init \" +\n\t\t\t\"                 set my.foo to 42 \" +\n\t\t\t\"               end\" +\n\t\t\t\"               on click put my.foo into my.innerHTML'></div>\"\n\t\t)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "init",
    "name": "can initialize immediately",
    "html": "<script type='text/hyperscript'>init immediately set window.bar to 42 end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>init immediately set window.bar to 42 end</script>\"\n\t\t)\n\t\t// immediately runs synchronously during processNode, so bar is set before the microtask\n\t\tconst bar = await evaluate(() => window.bar)\n\t\texpect(bar).toBe(42)\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "integration/htmx",
    "name": "htmx content added by hyperscript is processed and can issue requests",
    "html": "\n\t\t\t<button _=\"on click put '<div id=&quot;injected&quot; hx-get=&quot;/test9&quot; hx-trigger=&quot;click&quot; hx-target=&quot;#result9&quot;>Click me</div>' after me\">\n\t\t\t\tInject\n\t\t\t</button>\n\t\t\t<div id=\"result9\"></div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test9', '<span>from server</span>')\n\t\tawait html(`\n\t\t\t<button _=\"on click put '<div id=&quot;injected&quot; hx-get=&quot;/test9&quot; hx-trigger=&quot;click&quot; hx-target=&quot;#result9&quot;>Click me</div>' after me\">\n\t\t\t\tInject\n\t\t\t</button>\n\t\t\t<div id=\"result9\"></div>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#injected').count()).toBe(1)\n\t\tawait find('#injected').click()\n\t\tawait expect.poll(() => find('#result9 span').textContent()).toBe('from server')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript can cancel htmx request via halt",
    "html": "\n\t\t\t<button hx-get=\"/test5\" hx-target=\"#out5\"\n\t\t\t        _=\"on htmx:before:request halt the event\">Load</button>\n\t\t\t<div id=\"out5\">original</div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test5', '<p>should not appear</p>')\n\t\tawait html(`\n\t\t\t<button hx-get=\"/test5\" hx-target=\"#out5\"\n\t\t\t        _=\"on htmx:before:request halt the event\">Load</button>\n\t\t\t<div id=\"out5\">original</div>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 200)))\n\t\tawait expect(find('#out5')).toHaveText('original')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript can modify form values before htmx submit",
    "html": "\n\t\t\t<form hx-post=\"/test4\" hx-target=\"#result4\"\n\t\t\t      _=\"on submit set #hidden4's value to 'injected'\">\n\t\t\t\t<input type=\"hidden\" id=\"hidden4\" name=\"data\" value=\"\" />\n\t\t\t\t<button type=\"submit\">Submit</button>\n\t\t\t</form>\n\t\t\t<div id=\"result4\"></div>\n\t\t",
    "body": "\n\t\tawait mock('POST', '/test4', '<span>posted</span>')\n\t\tawait html(`\n\t\t\t<form hx-post=\"/test4\" hx-target=\"#result4\"\n\t\t\t      _=\"on submit set #hidden4's value to 'injected'\">\n\t\t\t\t<input type=\"hidden\" id=\"hidden4\" name=\"data\" value=\"\" />\n\t\t\t\t<button type=\"submit\">Submit</button>\n\t\t\t</form>\n\t\t\t<div id=\"result4\"></div>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#result4 span').textContent()).toBe('posted')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript can react to htmx:after:request",
    "html": "\n\t\t\t<button hx-get=\"/test8\" hx-target=\"#out8\"\n\t\t\t        _=\"on htmx:after:request put 'finished' into #status8\">Send</button>\n\t\t\t<div id=\"out8\"></div>\n\t\t\t<span id=\"status8\"></span>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test8', '<p>done</p>')\n\t\tawait html(`\n\t\t\t<button hx-get=\"/test8\" hx-target=\"#out8\"\n\t\t\t        _=\"on htmx:after:request put 'finished' into #status8\">Send</button>\n\t\t\t<div id=\"out8\"></div>\n\t\t\t<span id=\"status8\"></span>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#status8').textContent()).toBe('finished')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript can trigger htmx requests via send",
    "html": "\n\t\t\t<div hx-get=\"/test2\" hx-trigger=\"doLoad\" hx-target=\"#out2\">waiting</div>\n\t\t\t<button _=\"on click send doLoad to the previous <div/>\">Go</button>\n\t\t\t<div id=\"out2\"></div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test2', '<p>got it</p>')\n\t\tawait html(`\n\t\t\t<div hx-get=\"/test2\" hx-trigger=\"doLoad\" hx-target=\"#out2\">waiting</div>\n\t\t\t<button _=\"on click send doLoad to the previous <div/>\">Go</button>\n\t\t\t<div id=\"out2\"></div>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#out2 p').textContent()).toBe('got it')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript element-scoped vars persist across htmx swaps",
    "html": "\n\t\t\t<div id=\"outer7\" _=\"set :counter to 0\n\t\t\t        on click from #clicker7 increment :counter then put :counter into #count7\">\n\t\t\t\t<button id=\"clicker7\">Count</button>\n\t\t\t\t<button id=\"swapper7\" hx-get=\"/test7\" hx-target=\"#target7\" hx-swap=\"innerHTML\">Swap</button>\n\t\t\t\t<div id=\"target7\">original</div>\n\t\t\t\t<span id=\"count7\">0</span>\n\t\t\t</div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test7', '<span>swapped</span>')\n\t\tawait html(`\n\t\t\t<div id=\"outer7\" _=\"set :counter to 0\n\t\t\t        on click from #clicker7 increment :counter then put :counter into #count7\">\n\t\t\t\t<button id=\"clicker7\">Count</button>\n\t\t\t\t<button id=\"swapper7\" hx-get=\"/test7\" hx-target=\"#target7\" hx-swap=\"innerHTML\">Swap</button>\n\t\t\t\t<div id=\"target7\">original</div>\n\t\t\t\t<span id=\"count7\">0</span>\n\t\t\t</div>\n\t\t`)\n\t\tawait find('#clicker7').click()\n\t\tawait expect.poll(() => find('#count7').textContent()).toBe('1')\n\t\tawait find('#swapper7').click()\n\t\tawait expect.poll(() => find('#target7 span').textContent()).toBe('swapped')\n\t\tawait find('#clicker7').click()\n\t\tawait expect.poll(() => find('#count7').textContent()).toBe('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript responds to htmx swap events",
    "html": "\n\t\t\t<button hx-get=\"/test1\" hx-target=\"#target1\"\n\t\t\t        _=\"on htmx:after:swap put 'swapped!' into #status1\">Load</button>\n\t\t\t<div id=\"target1\"></div>\n\t\t\t<span id=\"status1\"></span>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test1', '<p>loaded</p>')\n\t\tawait html(`\n\t\t\t<button hx-get=\"/test1\" hx-target=\"#target1\"\n\t\t\t        _=\"on htmx:after:swap put 'swapped!' into #status1\">Load</button>\n\t\t\t<div id=\"target1\"></div>\n\t\t\t<span id=\"status1\"></span>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#status1').textContent()).toBe('swapped!')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript responds to htmx:after:settle on target",
    "html": "\n\t\t\t<button hx-get=\"/test6\" hx-target=\"#target6\">Load</button>\n\t\t\t<div id=\"target6\" _=\"on htmx:after:settle add .done to me\"></div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test6', '<span class=\"new\">content</span>')\n\t\tawait html(`\n\t\t\t<button hx-get=\"/test6\" hx-target=\"#target6\">Load</button>\n\t\t\t<div id=\"target6\" _=\"on htmx:after:settle add .done to me\"></div>\n\t\t`)\n\t\tawait find('button').click()\n\t\tawait expect.poll(() => find('#target6').getAttribute('class')).toContain('done')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "integration/htmx",
    "name": "hyperscript works on htmx-swapped content",
    "html": "\n\t\t\t<button hx-get=\"/test3\" hx-target=\"#container3\">Load</button>\n\t\t\t<div id=\"container3\"></div>\n\t\t",
    "body": "\n\t\tawait mock('GET', '/test3', '<button id=\"inner\" _=\"on click put \\'clicked\\' into me\">Click me</button>')\n\t\tawait html(`\n\t\t\t<button hx-get=\"/test3\" hx-target=\"#container3\">Load</button>\n\t\t\t<div id=\"container3\"></div>\n\t\t`)\n\t\tawait find('button').first().click()\n\t\tawait expect.poll(() => find('#inner').count()).toBe(1)\n\t\tawait find('#inner').click()\n\t\tawait expect.poll(() => find('#inner').textContent()).toBe('clicked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can access values from _hyperscript",
    "html": "<div _='on click set t to true        then js(t) window.testSuccess = t end'></div>",
    "body": "\n\t\tawait evaluate(() => window.testSuccess = false);\n\t\tawait html(\"<div _='on click set t to true \" + \"       then js(t) window.testSuccess = t end'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.testSuccess)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can deal with empty input list",
    "html": "<div _=\"on click js() window.testSuccess = true end\"></div>",
    "body": "\n\t\tawait evaluate(() => window.testSuccess = false);\n\t\tawait html('<div _=\"on click js() window.testSuccess = true end\"></div>');\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.testSuccess)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can do both of the above",
    "html": "<div _=\"on click set a to 1          then js(a) return a + 1 end          then put it into my.innerHTML\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\"on click set a to 1 ' +\n\t\t\t\t\"         then js(a) return a + 1 end \" +\n\t\t\t\t'         then put it into my.innerHTML\"></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can expose functions",
    "html": "<script type=text/hyperscript>  js     function foo() {       return 'test succeeded';     }     return { foo: foo };   end ",
    "body": "\n\t\tawait html(\"<script type=text/hyperscript>  js     function foo() {       return 'test succeeded';     }     return { foo: foo };   end \")\n\t\tconst result = await evaluate(() => window.foo())\n\t\texpect(result).toBe('test succeeded')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can expose globals",
    "html": "<script type=text/hyperscript>  js     return { foo: 'test succeeded' };   end ",
    "body": "\n\t\tawait html(\"<script type=text/hyperscript>  js     return { foo: 'test succeeded' };   end \")\n\t\tconst result = await evaluate(() => window.foo)\n\t\texpect(result).toBe('test succeeded')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can hide functions",
    "html": "<script type=text/hyperscript>  js     function bar() {}     function foo() {       return 'test succeeded';     }     return { foo: foo };   end ",
    "body": "\n\t\tawait html(\"<script type=text/hyperscript>  js     function bar() {}     function foo() {       return 'test succeeded';     }     return { foo: foo };   end \")\n\t\tconst result = await evaluate(() => window.foo())\n\t\texpect(result).toBe('test succeeded')\n\t\tconst hasBar = await evaluate(() => 'bar' in window)\n\t\texpect(hasBar).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can return values to _hyperscript",
    "html": "<div _=\"on click js return 'test success' end         then put it into my.innerHTML\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _=\\\"on click js return 'test success' end \" + '        then put it into my.innerHTML\"></div>'\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"test success\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can run js",
    "html": "<div _=\"on click js window.testSuccess = true end\"></div>",
    "body": "\n\t\tawait evaluate(() => window.testSuccess = false);\n\t\tawait html('<div _=\"on click js window.testSuccess = true end\"></div>');\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.testSuccess)).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "can run js at the top level",
    "html": "<script type=text/hyperscript>  js     window.testSuccess = true   end ",
    "body": "\n\t\tawait evaluate(() => window.testSuccess = false)\n\t\tawait html(\"<script type=text/hyperscript>  js     window.testSuccess = true   end \")\n\t\tconst result = await evaluate(() => window.testSuccess)\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "does not expose variables",
    "html": "<script type=text/hyperscript>  js     var foo = 'foo'     let bar = 'bar'     const baz = 'baz'   end ",
    "body": "\n\t\tawait html(\"<script type=text/hyperscript>  js     var foo = 'foo'     let bar = 'bar'     const baz = 'baz'   end \")\n\t\tconst hasFoo = await evaluate(() => 'foo' in window)\n\t\tconst hasBar = await evaluate(() => 'bar' in window)\n\t\tconst hasBaz = await evaluate(() => 'baz' in window)\n\t\texpect(hasFoo).toBe(false)\n\t\texpect(hasBar).toBe(false)\n\t\texpect(hasBaz).toBe(false)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "js",
    "name": "handles rejected promises without hanging",
    "html": "<div _='on click js return Promise.reject(\"boom\") end catch e put e into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click js return Promise.reject(\\\"boom\\\") end \" +\n\t\t\t\"catch e put e into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"boom\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "append triggers live block",
    "html": "<div _=\"live put $items.join(', ') into me\"></div>",
    "body": "\n\t\tawait run(\"set $items to ['a', 'b']\")\n\t\tawait html(`<div _=\"live put $items.join(', ') into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b')\n\n\t\tawait run(\"append 'c' to $items\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b, c')\n\t\tawait evaluate(() => { delete window.$items })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "array + still works with live",
    "html": "<div _=\"live put $items.join(', ') into me\"></div>",
    "body": "\n\t\tawait run(\"set $items to ['a', 'b']\")\n\t\tawait html(`<div _=\"live put $items.join(', ') into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b')\n\n\t\tawait run(\"set $items to $items + ['c']\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b, c')\n\t\tawait evaluate(() => { delete window.$items })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "bind and live on same element do not interfere",
    "html": "<input type=\"text\" value=\"alice\"\n\t\t\t        _=\"bind $username to me end\n\t\t\t           live set my @data-mirror to $username\" /><span _=\"when $username changes put it into me\"></span>",
    "body": "\n\t\tawait run(\"set $username to 'alice'\")\n\t\tawait html(\n\t\t\t`<input type=\"text\" value=\"alice\"\n\t\t\t        _=\"bind $username to me end\n\t\t\t           live set my @data-mirror to $username\" />` +\n\t\t\t`<span _=\"when $username changes put it into me\"></span>`\n\t\t)\n\t\tawait expect.poll(() => find('span').textContent()).toBe('alice')\n\t\tawait expect.poll(() => find('input').getAttribute('data-mirror')).toBe('alice')\n\n\t\tawait find('input').fill('bob')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('bob')\n\t\tawait expect.poll(() => find('input').getAttribute('data-mirror')).toBe('bob')\n\t\tawait evaluate(() => { delete window.$username })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "block form cascades inter-dependent commands",
    "html": "<div _=\"live\n\t\t\t           set $subtotal to ($price * $qty)\n\t\t\t           set $total to ($subtotal + $tax)\n\t\t\t         end\n\t\t\t         when $total changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $price to 10\")\n\t\tawait run(\"set $qty to 3\")\n\t\tawait run(\"set $tax to 5\")\n\t\tawait html(\n\t\t\t`<div _=\"live\n\t\t\t           set $subtotal to ($price * $qty)\n\t\t\t           set $total to ($subtotal + $tax)\n\t\t\t         end\n\t\t\t         when $total changes put it into me\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('35')\n\n\t\tawait run(\"set $price to 20\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('65')\n\n\t\tawait run(\"set $tax to 10\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('70')\n\t\tawait evaluate(() => {\n\t\t\tdelete window.$price; delete window.$qty; delete window.$tax\n\t\t\tdelete window.$subtotal; delete window.$total\n\t\t})\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "block form re-runs all commands when any dependency changes",
    "html": "<span id=\"w\" _=\"when $doubleWidth changes put it into me\"></span><span id=\"h\" _=\"when $doubleHeight changes put it into me\"></span><div _=\"live\n\t\t\t           set $doubleWidth to ($width * 2)\n\t\t\t           set $doubleHeight to ($height * 2)\n\t\t\t         end\"></div>",
    "body": "\n\t\tawait run(\"set $width to 100\")\n\t\tawait run(\"set $height to 200\")\n\t\tawait html(\n\t\t\t`<span id=\"w\" _=\"when $doubleWidth changes put it into me\"></span>` +\n\t\t\t`<span id=\"h\" _=\"when $doubleHeight changes put it into me\"></span>` +\n\t\t\t`<div _=\"live\n\t\t\t           set $doubleWidth to ($width * 2)\n\t\t\t           set $doubleHeight to ($height * 2)\n\t\t\t         end\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('#w').textContent()).toBe('200')\n\t\tawait expect.poll(() => find('#h').textContent()).toBe('400')\n\n\t\t// Change $height - both commands re-run, but $doubleWidth stays the same (dedup)\n\t\tawait run(\"set $height to 300\")\n\t\tawait expect.poll(() => find('#h').textContent()).toBe('600')\n\t\tawait expect(find('#w')).toHaveText('200') // unchanged because dedup\n\n\t\tawait evaluate(() => {\n\t\t\tdelete window.$width; delete window.$height\n\t\t\tdelete window.$doubleWidth; delete window.$doubleHeight\n\t\t})\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "conditional branch only tracks the active dependency",
    "html": "<div _=\"live\n\t\t\t           if $showFirst put $firstName into me else put $lastName into me end\n\t\t\t         end\"></div>",
    "body": "\n\t\tawait run(\"set $showFirst to true\")\n\t\tawait run(\"set $firstName to 'Alice'\")\n\t\tawait run(\"set $lastName to 'Smith'\")\n\t\tawait html(\n\t\t\t`<div _=\"live\n\t\t\t           if $showFirst put $firstName into me else put $lastName into me end\n\t\t\t         end\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Alice')\n\n\t\t// Active branch: $firstName triggers\n\t\tawait run(\"set $firstName to 'Bob'\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Bob')\n\n\t\t// Inactive branch: $lastName does NOT trigger\n\t\tawait run(\"set $lastName to 'Jones'\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveText('Bob')\n\n\t\t// Flip condition\n\t\tawait run(\"set $showFirst to false\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Jones')\n\n\t\t// Now $firstName is inactive\n\t\tawait run(\"set $firstName to 'Charlie'\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).toHaveText('Jones')\n\n\t\tawait evaluate(() => {\n\t\t\tdelete window.$showFirst; delete window.$firstName; delete window.$lastName\n\t\t})\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "derives a variable from a computed expression",
    "html": "<div _=\"live set $total to ($price * $qty) end\n\t\t\t         when $total changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $price to 10\")\n\t\tawait run(\"set $qty to 3\")\n\t\tawait html(\n\t\t\t`<div _=\"live set $total to ($price * $qty) end\n\t\t\t         when $total changes put it into me\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveText('30')\n\n\t\tawait run(\"set $price to 25\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('75')\n\t\tawait evaluate(() => { delete window.$price; delete window.$qty; delete window.$total })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "effects stop when element is removed from DOM",
    "html": "<div _=\"live put $message into me\"></div>",
    "body": "\n\t\tawait run(\"set $message to 'initial'\")\n\t\tawait html(`<div _=\"live put $message into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('initial')\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').remove())\n\t\tawait run(\"set $message to 'after removal'\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait evaluate(() => { delete window.$message })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "live and when on same element do not interfere",
    "html": "<div _=\"live set my @data-status to $status end\n\t\t\t         when $status changes put 'Status: ' + it into me\"></div>",
    "body": "\n\t\tawait run(\"set $status to 'online'\")\n\t\tawait html(\n\t\t\t`<div _=\"live set my @data-status to $status end\n\t\t\t         when $status changes put 'Status: ' + it into me\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveAttribute('data-status', 'online')\n\t\tawait expect(find('div')).toHaveText('Status: online')\n\n\t\tawait run(\"set $status to 'offline'\")\n\t\tawait expect.poll(() => find('div').getAttribute('data-status')).toBe('offline')\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Status: offline')\n\t\tawait evaluate(() => { delete window.$status })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "multiple live on same element work independently",
    "html": "<div _=\"live set my @data-name to $firstName end\n\t\t\t         live set my @data-age to $age\"></div>",
    "body": "\n\t\tawait run(\"set $firstName to 'Alice'\")\n\t\tawait run(\"set $age to 30\")\n\t\tawait html(\n\t\t\t`<div _=\"live set my @data-name to $firstName end\n\t\t\t         live set my @data-age to $age\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveAttribute('data-name', 'Alice')\n\t\tawait expect(find('div')).toHaveAttribute('data-age', '30')\n\n\t\tawait run(\"set $firstName to 'Bob'\")\n\t\tawait expect.poll(() => find('div').getAttribute('data-name')).toBe('Bob')\n\t\tawait expect(find('div')).toHaveAttribute('data-age', '30')\n\t\tawait evaluate(() => { delete window.$firstName; delete window.$age })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "property change on object in array triggers live re-render",
    "html": "<script type=\"text/hyperscript-template\" id=\"people-tmpl\">#for p in people\n<span>${p.name}</span>\n#end</script><div _=\"live render #people-tmpl with people: $people then put it into my.innerHTML end\"></div>",
    "body": "\n\t\tawait run(\"set $people to [{name: 'Alice'}, {name: 'Bob'}]\")\n\t\tawait html(\n\t\t\t`<script type=\"text/hyperscript-template\" id=\"people-tmpl\">#for p in people\\n<span>\\${p.name}</span>\\n#end</script>` +\n\t\t\t`<div _=\"live render #people-tmpl with people: $people then put it into my.innerHTML end\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('div').textContent()).toContain('Alice')\n\t\tawait expect.poll(() => find('div').textContent()).toContain('Bob')\n\n\t\tawait run(\"set $people[0].name to 'Charlie'\")\n\t\tawait expect.poll(() => find('div').textContent()).toContain('Charlie')\n\t\tawait expect.poll(() => find('div').textContent()).toContain('Bob')\n\t\tawait evaluate(() => { delete window.$people })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "push object then modify its property both trigger live",
    "html": "<script type=\"text/hyperscript-template\" id=\"items-tmpl\">#for item in items\n<span>${item.label}</span>\n#end</script><div _=\"live render #items-tmpl with items: $items then put it into my.innerHTML end\"></div>",
    "body": "\n\t\tawait run(\"set $items to [{label: 'first'}]\")\n\t\tawait html(\n\t\t\t`<script type=\"text/hyperscript-template\" id=\"items-tmpl\">#for item in items\\n<span>\\${item.label}</span>\\n#end</script>` +\n\t\t\t`<div _=\"live render #items-tmpl with items: $items then put it into my.innerHTML end\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('div').textContent()).toContain('first')\n\n\t\t// Push triggers re-render\n\t\tawait run(\"call $items.push({label: 'second'})\")\n\t\tawait expect.poll(() => find('div').textContent()).toContain('second')\n\n\t\t// Modify property on new object triggers re-render\n\t\tawait run(\"set $items[1].label to 'updated'\")\n\t\tawait expect.poll(() => find('div').textContent()).toContain('updated')\n\t\tawait evaluate(() => { delete window.$items })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "push via call triggers live block",
    "html": "<div _=\"live put $items.join(', ') into me\"></div>",
    "body": "\n\t\tawait run(\"set $items to ['a', 'b']\")\n\t\tawait html(`<div _=\"live put $items.join(', ') into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b')\n\n\t\tawait run(\"call $items.push('c')\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b, c')\n\t\tawait evaluate(() => { delete window.$items })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "push via pseudo-command triggers live block",
    "html": "<div _=\"live put $items.join(', ') into me\"></div>",
    "body": "\n\t\tawait run(\"set $items to ['a', 'b']\")\n\t\tawait html(`<div _=\"live put $items.join(', ') into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b')\n\n\t\tawait run(\"$items.push('c')\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('a, b, c')\n\t\tawait evaluate(() => { delete window.$items })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "puts a computed dollar amount into the DOM",
    "html": "<div _=\"live put '$' + ($price * $qty) into me\"></div>",
    "body": "\n\t\tawait run(\"set $price to 10\")\n\t\tawait run(\"set $qty to 2\")\n\t\tawait html(`<div _=\"live put '$' + ($price * $qty) into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('$20')\n\n\t\tawait run(\"set $qty to 5\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('$50')\n\t\tawait evaluate(() => { delete window.$price; delete window.$qty })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "reactive effects are stopped on cleanup",
    "html": "<div id=\"d1\" _=\"live put $count into me\"></div>",
    "body": "\n\t\tawait run(\"set $count to 0\")\n\t\tawait html(`<div id=\"d1\" _=\"live put $count into me\"></div>`)\n\t\tawait expect.poll(() => find('#d1').textContent()).toBe('0')\n\n\t\t// Clean up the element (but keep it in DOM to detect if effect still fires)\n\t\tawait evaluate(() => {\n\t\t\tvar el = document.querySelector('#work-area #d1')\n\t\t\t_hyperscript.internals.runtime.cleanup(el)\n\t\t\tel.textContent = 'cleaned'\n\t\t})\n\n\t\t// Change the variable - the effect should NOT fire since we cleaned up\n\t\tawait run(\"set $count to 99\")\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\n\t\t// If effects were properly stopped, text stays 'cleaned'\n\t\tawait expect(find('#d1')).toHaveText('cleaned')\n\t\tawait evaluate(() => { delete window.$count })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "separate live statements create independent effects",
    "html": "<span id=\"w\" _=\"when $doubleWidth changes put it into me\"></span><span id=\"h\" _=\"when $doubleHeight changes put it into me\"></span><div _=\"live set $doubleWidth to ($width * 2) end\n\t\t\t         live set $doubleHeight to ($height * 2)\"></div>",
    "body": "\n\t\tawait run(\"set $width to 100\")\n\t\tawait run(\"set $height to 200\")\n\t\tawait html(\n\t\t\t`<span id=\"w\" _=\"when $doubleWidth changes put it into me\"></span>` +\n\t\t\t`<span id=\"h\" _=\"when $doubleHeight changes put it into me\"></span>` +\n\t\t\t`<div _=\"live set $doubleWidth to ($width * 2) end\n\t\t\t         live set $doubleHeight to ($height * 2)\"></div>`\n\t\t)\n\t\tawait expect.poll(() => find('#w').textContent()).toBe('200')\n\t\tawait expect.poll(() => find('#h').textContent()).toBe('400')\n\n\t\t// Change only $height - only $doubleHeight updates\n\t\tawait run(\"set $height to 300\")\n\t\tawait expect.poll(() => find('#h').textContent()).toBe('600')\n\t\tawait expect(find('#w')).toHaveText('200')\n\n\t\tawait evaluate(() => {\n\t\t\tdelete window.$width; delete window.$height\n\t\t\tdelete window.$doubleWidth; delete window.$doubleHeight\n\t\t})\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "set property still works with live",
    "html": "<div _=\"live put $obj.name into me\"></div>",
    "body": "\n\t\tawait run(\"set $obj to {name: 'Alice'}\")\n\t\tawait html(`<div _=\"live put $obj.name into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Alice')\n\n\t\tawait run(\"set $obj.name to 'Bob'\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('Bob')\n\t\tawait evaluate(() => { delete window.$obj })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "sets a style reactively",
    "html": "<div _=\"live set *opacity to $opacity\">visible</div>",
    "body": "\n\t\tawait run(\"set $opacity to 1\")\n\t\tawait html(`<div _=\"live set *opacity to $opacity\">visible</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\n\t\tawait run(\"set $opacity to 0.5\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div').style.opacity)\n\t\t).toBe('0.5')\n\t\tawait evaluate(() => { delete window.$opacity })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "sets an attribute reactively",
    "html": "<div _=\"live set my @data-theme to $theme\"></div>",
    "body": "\n\t\tawait run(\"set $theme to 'light'\")\n\t\tawait html(`<div _=\"live set my @data-theme to $theme\"></div>`)\n\t\tawait expect(find('div')).toHaveAttribute('data-theme', 'light')\n\n\t\tawait run(\"set $theme to 'dark'\")\n\t\tawait expect.poll(() => find('div').getAttribute('data-theme')).toBe('dark')\n\t\tawait evaluate(() => { delete window.$theme })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "toggles a class based on a boolean variable",
    "html": "<div _=\"live\n\t\t\t           if $isActive add .active to me else remove .active from me end\n\t\t\t         end\">test</div>",
    "body": "\n\t\tawait run(\"set $isActive to false\")\n\t\tawait html(\n\t\t\t`<div _=\"live\n\t\t\t           if $isActive add .active to me else remove .active from me end\n\t\t\t         end\">test</div>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('div')).not.toHaveClass('active')\n\n\t\tawait run(\"set $isActive to true\")\n\t\tawait expect.poll(() => find('div').getAttribute('class')).toContain('active')\n\n\t\tawait run(\"set $isActive to false\")\n\t\tawait expect.poll(() => find('div').getAttribute('class') || '').not.toContain('active')\n\t\tawait evaluate(() => { delete window.$isActive })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "live",
    "name": "toggles display style based on a boolean variable",
    "html": "<div _=\"live\n\t\t\t           if $isVisible set *display to 'block' else set *display to 'none' end\n\t\t\t         end\">content</div>",
    "body": "\n\t\tawait run(\"set $isVisible to true\")\n\t\tawait html(\n\t\t\t`<div _=\"live\n\t\t\t           if $isVisible set *display to 'block' else set *display to 'none' end\n\t\t\t         end\">content</div>`\n\t\t)\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div').style.display)\n\t\t).toBe('block')\n\n\t\tawait run(\"set $isVisible to false\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div').style.display)\n\t\t).toBe('none')\n\n\t\tawait run(\"set $isVisible to true\")\n\t\tawait expect.poll(() =>\n\t\t\tevaluate(() => document.querySelector('#work-area div').style.display)\n\t\t).toBe('block')\n\t\tawait evaluate(() => { delete window.$isVisible })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "live",
    "name": "updates DOM text reactively with put",
    "html": "<div _=\"live put 'hello ' + $greeting into me\"></div>",
    "body": "\n\t\tawait run(\"set $greeting to 'world'\")\n\t\tawait html(`<div _=\"live put 'hello ' + $greeting into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('hello world')\n\n\t\tawait run(\"set $greeting to 'there'\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('hello there')\n\t\tawait evaluate(() => { delete window.$greeting })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "log",
    "name": "can log multiple items",
    "html": "<div _='on click log me, my'></div>",
    "body": "\n\t\tawait html(\"<div _='on click log me, my'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "log",
    "name": "can log multiple items with debug",
    "html": "<div _='on click log me, my with console.debug'></div>",
    "body": "\n\t\tawait html(\"<div _='on click log me, my with console.debug'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "log",
    "name": "can log multiple items with error",
    "html": "<div _='on click log me, my with console.error'></div>",
    "body": "\n\t\tawait html(\"<div _='on click log me, my with console.error'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "log",
    "name": "can log single item",
    "html": "<div _='on click log me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click log me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "make",
    "name": "can make elements",
    "html": "",
    "body": "\n\t\tawait run(`make a <p/> set window.obj to it`);\n\t\tconst tagName = await evaluate(() => window.obj.tagName);\n\t\texpect(tagName).toBe(\"P\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make elements with id and classes",
    "html": "",
    "body": "\n\t\tawait run(`make a <p.a#id.b.c/> set window.obj to it`);\n\t\tconst tagName = await evaluate(() => window.obj.tagName);\n\t\texpect(tagName).toBe(\"P\");\n\t\tconst id = await evaluate(() => window.obj.id);\n\t\texpect(id).toBe(\"id\");\n\t\tconst hasA = await evaluate(() => window.obj.classList.contains(\"a\"));\n\t\texpect(hasA).toBe(true);\n\t\tconst hasB = await evaluate(() => window.obj.classList.contains(\"b\"));\n\t\texpect(hasB).toBe(true);\n\t\tconst hasC = await evaluate(() => window.obj.classList.contains(\"c\"));\n\t\texpect(hasC).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make named objects",
    "html": "",
    "body": "\n\t\tawait run(`make a WeakMap called wm then set window.obj to wm`);\n\t\tconst isWeakMap = await evaluate(() => window.obj instanceof WeakMap);\n\t\texpect(isWeakMap).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make named objects w/ global scope",
    "html": "",
    "body": "\n\t\tawait run(`make a WeakMap called $wm`);\n\t\tconst isWeakMap = await evaluate(() => window['$wm'] instanceof WeakMap);\n\t\texpect(isWeakMap).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make named objects with arguments",
    "html": "",
    "body": "\n\t\tawait run(`make a URL from \"/playground/\", \"https://hyperscript.org/\" called u\n\t\t\tset window.obj to u`);\n\t\tconst isURL = await evaluate(() => window.obj instanceof URL);\n\t\texpect(isURL).toBe(true);\n\t\tconst href = await evaluate(() => window.obj.href);\n\t\texpect(href).toBe(\"https://hyperscript.org/playground/\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make objects",
    "html": "",
    "body": "\n\t\tawait run(`make a WeakMap then set window.obj to it`);\n\t\tconst isWeakMap = await evaluate(() => window.obj instanceof WeakMap);\n\t\texpect(isWeakMap).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "can make objects with arguments",
    "html": "",
    "body": "\n\t\tawait run(`make a URL from \"/playground/\", \"https://hyperscript.org/\"\n\t\t\tset window.obj to it`);\n\t\tconst isURL = await evaluate(() => window.obj instanceof URL);\n\t\texpect(isURL).toBe(true);\n\t\tconst href = await evaluate(() => window.obj.href);\n\t\texpect(href).toBe(\"https://hyperscript.org/playground/\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "make",
    "name": "creates a div by default",
    "html": "",
    "body": "\n\t\tawait run(`make a <.a/> set window.obj to it`);\n\t\tconst tagName = await evaluate(() => window.obj.tagName);\n\t\texpect(tagName).toBe(\"DIV\");\n\t\tconst hasA = await evaluate(() => window.obj.classList.contains(\"a\"));\n\t\texpect(hasA).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "measure",
    "name": "can assign measurements to locals",
    "html": "<div _='on click measure my x,y,left,top,right,bottom         set window.measurement to {left:left,top:top,right:right,bottom:bottom}'></div> ",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click measure my x,y,left,top,right,bottom \" +\n\t\t\t\t\"        set window.measurement to {left:left,top:top,right:right,bottom:bottom}'></div> \"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tconst hasProps = await evaluate(() => {\n\t\t\tconst m = window.measurement;\n\t\t\treturn m.hasOwnProperty('top') && m.hasOwnProperty('left') &&\n\t\t\t\tm.hasOwnProperty('right') && m.hasOwnProperty('bottom');\n\t\t});\n\t\texpect(hasProps).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "measure",
    "name": "can measure all the supported properties",
    "html": "<div _='on click measure x,y,left,top,right,bottom,width,height,bounds,scrollLeft,scrollTop,scrollLeftMax,scrollTopMax,scrollWidth,scrollHeight,scroll'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click measure x,y,left,top,right,bottom,width,height,bounds,scrollLeft,scrollTop,scrollLeftMax,scrollTopMax,scrollWidth,scrollHeight,scroll'></div>\"\n\t\t);\n\t\t// Should not throw\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "measure",
    "name": "can measure another element",
    "html": "<div id='other' style='all: initial; position: fixed; top: 89px'></div><div _='on click measure #other then set window.measurement to it'></div> ",
    "body": "\n\t\tawait html(\"<div id='other' style='all: initial; position: fixed; top: 89px'></div>\" +\n\t\t\t\"<div _='on click measure #other then set window.measurement to it'></div> \");\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tconst top = await evaluate(() => Math.round(window.measurement.top));\n\t\texpect(top).toBe(89);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "measure",
    "name": "can measure me",
    "html": "<div style='all: initial; position: fixed; top: 89px' _='on click   measure me then set window.measurement to it'></div> ",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='all: initial; position: fixed; top: 89px' _='on click \" +\n\t\t\t\t\"  measure me then set window.measurement to it'></div> \"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tconst top = await evaluate(() => Math.round(window.measurement.top));\n\t\texpect(top).toBe(89);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "measure",
    "name": "can measure with of syntax",
    "html": "<div id='other' style='all: initial; position: fixed; top: 89px'></div><div _='on click measure top of #other then set window.measurement to {top: top}'></div>",
    "body": "\n\t\tawait html(\"<div id='other' style='all: initial; position: fixed; top: 89px'></div>\" +\n\t\t\t\"<div _='on click measure top of #other then set window.measurement to {top: top}'></div>\");\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tconst top = await evaluate(() => Math.round(window.measurement.top));\n\t\texpect(top).toBe(89);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "measure",
    "name": "can measure with possessive syntax",
    "html": "<div id='other' style='all: initial; position: fixed; top: 89px'></div><div _=\"on click measure #other's top then set window.measurement to {top: top}\"></div>",
    "body": "\n\t\tawait html(\"<div id='other' style='all: initial; position: fixed; top: 89px'></div>\" +\n\t\t\t'<div _=\"on click measure #other\\'s top then set window.measurement to {top: top}\"></div>');\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tconst top = await evaluate(() => Math.round(window.measurement.top));\n\t\texpect(top).toBe(89);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "basic morph updates content",
    "html": "<div id='target'>old</div><button _='on click morph #target to \"<div id=target>new</div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button _='on click morph #target to \\\"<div id=target>new</div>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"new\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph adds new children",
    "html": "<div id='target'><span>first</span></div><button id='go' _=\"on click morph #target to '<div id=target><span>first</span><span>second</span></div>'\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'><span>first</span></div>\" +\n\t\t\t\"<button id='go' _=\\\"on click morph #target to '<div id=target><span>first</span><span>second</span></div>'\\\">go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar count = await evaluate(() => document.querySelectorAll('#target span').length);\n\t\texpect(count).toBe(2);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph cleans up removed hyperscript elements",
    "html": "<div id='target'>  <div id='child' _='on click put \"alive\" into me'>child</div></div><button _='on click morph #target to \"<div id=target><p>replaced</p></div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>\" +\n\t\t\t\"  <div id='child' _='on click put \\\"alive\\\" into me'>child</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button _='on click morph #target to \\\"<div id=target><p>replaced</p></div>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar gone = await evaluate(() => document.querySelector('#child'));\n\t\texpect(gone).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph initializes hyperscript on new elements",
    "html": "<div id='target'><p>old</p></div><button id='go' _=\"on click morph #target to '<div id=target><p id=inner _=&#34;on click put `clicked` into me&#34;>new</p></div>'\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'><p>old</p></div>\" +\n\t\t\t\"<button id='go' _=\\\"on click morph #target to '<div id=target><p id=inner _=&#34;on click put `clicked` into me&#34;>new</p></div>'\\\">go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tawait expect(find('#inner')).toHaveText(\"new\");\n\t\tawait find('#inner').dispatchEvent('click');\n\t\tawait expect(find('#inner')).toHaveText(\"clicked\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph preserves element identity",
    "html": "<div id='target'>old</div><button id='go' _='on click morph #target to \"<div id=target>new</div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>old</div>\" +\n\t\t\t\"<button id='go' _='on click morph #target to \\\"<div id=target>new</div>\\\"'>go</button>\"\n\t\t);\n\t\t// Save a reference before morphing\n\t\tawait evaluate(() => { window._savedRef = document.querySelector('#target'); });\n\t\tawait find('#go').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"new\");\n\t\tvar same = await evaluate(() => document.querySelector('#target') === window._savedRef);\n\t\texpect(same).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph preserves matched child identity",
    "html": "<div id='target'><div id='child'>old</div></div><button id='go' _='on click morph #target to \"<div id=target><div id=child>new</div></div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'><div id='child'>old</div></div>\" +\n\t\t\t\"<button id='go' _='on click morph #target to \\\"<div id=target><div id=child>new</div></div>\\\"'>go</button>\"\n\t\t);\n\t\tawait evaluate(() => { window._savedChild = document.querySelector('#child'); });\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar result = await evaluate(() => ({\n\t\t\tsame: document.querySelector('#child') === window._savedChild,\n\t\t\ttext: document.querySelector('#child').textContent\n\t\t}));\n\t\texpect(result.same).toBe(true);\n\t\texpect(result.text).toBe(\"new\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph removes old children",
    "html": "<div id='target'><span>first</span><span>second</span></div><button id='go' _=\"on click morph #target to '<div id=target><span>first</span></div>'\">go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'><span>first</span><span>second</span></div>\" +\n\t\t\t\"<button id='go' _=\\\"on click morph #target to '<div id=target><span>first</span></div>'\\\">go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar count = await evaluate(() => document.querySelectorAll('#target span').length);\n\t\texpect(count).toBe(1);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph reorders children by id",
    "html": "<div id='target'>  <div id='a'>A</div>  <div id='b'>B</div></div><button _='on click morph #target to \"<div id=target><div id=b>B2</div><div id=a>A2</div></div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>\" +\n\t\t\t\"  <div id='a'>A</div>\" +\n\t\t\t\"  <div id='b'>B</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button _='on click morph #target to \\\"<div id=target><div id=b>B2</div><div id=a>A2</div></div>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tvar order = await evaluate(() =>\n\t\t\tArray.from(document.querySelectorAll('#target > div')).map(e => e.id)\n\t\t);\n\t\texpect(order).toEqual([\"b\", \"a\"]);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph updates attributes",
    "html": "<div id='target' class='old'>content</div><button _='on click morph #target to \"<div id=target class=new>content</div>\"'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target' class='old'>content</div>\" +\n\t\t\t\"<button _='on click morph #target to \\\"<div id=target class=new>content</div>\\\"'>go</button>\"\n\t\t);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveClass(\"new\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "morph",
    "name": "morph with variable content",
    "html": "<div id='target'>original</div><button id='go' _='on click set content to \"<div id=target>morphed</div>\" then morph #target to content'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='target'>original</div>\" +\n\t\t\t\"<button id='go' _='on click set content to \\\"<div id=target>morphed</div>\\\" then morph #target to content'>go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tawait expect(find('#target')).toHaveText(\"morphed\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "async basic finally blocks work",
    "html": "<button _='on click wait a tick then throw \"bar\"             finally put \"bar\" into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click wait a tick then throw \\\"bar\\\"\" +\n\t\t\t\"             finally put \\\"bar\\\" into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "async exceptions don't kill the event queue",
    "html": "<button _='on click                     increment :x                      if :x is 1                       wait 1ms then throw \"bar\"                     otherwise                       put \"success\" into me                    end                     catch e                       put e into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click \" +\n\t\t\t\"                    increment :x  \" +\n\t\t\t\"                    if :x is 1 \" +\n\t\t\t\"                      wait 1ms then throw \\\"bar\\\" \" +\n\t\t\t\"                    otherwise \" +\n\t\t\t\"                      put \\\"success\\\" into me\" +\n\t\t\t\"                    end \" +\n\t\t\t\"                    catch e \" +\n\t\t\t\"                      put e into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('success')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "async exceptions in finally block don't kill the event queue",
    "html": "<button _='on click                     increment :x                  finally                      if :x is 1                       wait 1ms then throw \"bar\"                     otherwise                       put \"success\" into me                    end                     '></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click \" +\n\t\t\t\"                    increment :x\" +\n\t\t\t\"                  finally  \" +\n\t\t\t\"                    if :x is 1 \" +\n\t\t\t\"                      wait 1ms then throw \\\"bar\\\" \" +\n\t\t\t\"                    otherwise \" +\n\t\t\t\"                      put \\\"success\\\" into me\" +\n\t\t\t\"                    end \" +\n\t\t\t\"                    '></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('success')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "async finally blocks work when exception thrown in catch",
    "html": "<button _='on click wait a tick then throw \"bar\"            catch e set :foo to \"foo\" then throw e            finally put :foo + \"bar\" into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click wait a tick then throw \\\"bar\\\"\" +\n\t\t\t\"            catch e set :foo to \\\"foo\\\" then throw e\" +\n\t\t\t\"            finally put :foo + \\\"bar\\\" into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('foobar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "basic finally blocks work",
    "html": "<button _='on click throw \"bar\"             finally put \"bar\" into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click throw \\\"bar\\\"\" +\n\t\t\t\"             finally put \\\"bar\\\" into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can be in a top level script tag",
    "html": "<script type='text/hyperscript'>on customEvent put \"Loaded\" into #loadedDemo.innerHTML</script><div id='loadedDemo'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>on customEvent put \\\"Loaded\\\" into #loadedDemo.innerHTML</script>\" +\n\t\t\t\"<div id='loadedDemo'></div>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tconst wa = document.querySelector('#work-area')\n\t\t\twa.dispatchEvent(new Event('customEvent', {bubbles: true}))\n\t\t})\n\t\tawait expect(find('#loadedDemo')).toHaveText('Loaded')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "on",
    "name": "can catch async top-level exceptions",
    "html": "<button _='on click wait 1ms then throw \"bar\" catch e put e into me'></button>",
    "body": "\n\t\tawait html(\"<button _='on click wait 1ms then throw \\\"bar\\\" catch e put e into me'></button>\")\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can catch exceptions thrown in hyperscript functions",
    "html": "<script type='text/hyperscript'>  def throwBar()    throw 'bar'  end</script><button _='on click throwBar() catch e put e into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>  def throwBar()    throw 'bar'  end</script>\" +\n\t\t\t\"<button _='on click throwBar() catch e put e into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "on",
    "name": "can catch exceptions thrown in js functions",
    "html": "<button _='on click throwBar() catch e put e into me'></button>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.throwBar = function() { throw \"bar\" }\n\t\t})\n\t\tawait html(\"<button _='on click throwBar() catch e put e into me'></button>\")\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can catch top-level exceptions",
    "html": "<button _='on click throw \"bar\" catch e put e into me'></button>",
    "body": "\n\t\tawait html(\"<button _='on click throw \\\"bar\\\" catch e put e into me'></button>\")\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can click after a positive event filter",
    "html": "<div id='pf' _='on foo(bar)[bar] put \"triggered\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='pf' _='on foo(bar)[bar] put \\\"triggered\\\" into my.innerHTML'></div>\")\n\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area #pf').dispatchEvent(new CustomEvent(\"foo\", { detail: { bar: false } }))\n\t\t})\n\t\tawait expect(find('#pf')).toHaveText('')\n\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area #pf').dispatchEvent(new CustomEvent(\"foo\", { detail: { bar: true } }))\n\t\t})\n\t\tawait expect(find('#pf')).toHaveText('triggered')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can filter events based on count",
    "html": "<div _='on click 1 put 1 + my.innerHTML as Int into my.innerHTML'>0</div>",
    "body": "\n\t\tawait html(\"<div _='on click 1 put 1 + my.innerHTML as Int into my.innerHTML'>0</div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can filter events based on count range",
    "html": "<div _='on click 1 to 2 put 1 + my.innerHTML as Int into my.innerHTML'>0</div>",
    "body": "\n\t\tawait html(\"<div _='on click 1 to 2 put 1 + my.innerHTML as Int into my.innerHTML'>0</div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can filter events based on unbounded count range",
    "html": "<div _='on click 2 and on put 1 + my.innerHTML as Int into my.innerHTML'>0</div>",
    "body": "\n\t\tawait html(\"<div _='on click 2 and on put 1 + my.innerHTML as Int into my.innerHTML'>0</div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('0')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can fire an event on load",
    "html": "<div id='d1' _='on load put \"Loaded\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on load put \\\"Loaded\\\" into my.innerHTML'></div>\")\n\t\tawait expect(find('#d1')).toHaveText('Loaded')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can handle an or after a from clause",
    "html": "<div id='d1'></div><div id='d2'></div><div id='result' _=' \ton click from #d1 or click from #d2 \t\t increment @count then put @count into me\t'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1'></div>\" +\n\t\t\t\"<div id='d2'></div>\" +\n\t\t\t\"<div id='result' _=' \" +\n\t\t\t\"\ton click from #d1 or click from #d2 \" +\n\t\t\t\"\t\t increment @count then put @count into me\" +\n\t\t\t\"\t'></div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#result')).toHaveText('1')\n\t\tawait find('#d2').dispatchEvent('click')\n\t\tawait expect(find('#result')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can have a simple event filter",
    "html": "<div id='d1' _='on click[false] log event then put \"Clicked\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click[false] log event then put \\\"Clicked\\\" into my.innerHTML'></div>\")\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveText('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can have multiple event handlers",
    "html": "<div _='on foo put increment() into my.innerHTML end                          on bar put increment() into my.innerHTML'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 1\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\t\tawait html(\n\t\t\t\"<div _='on foo put increment() into my.innerHTML end\" +\n\t\t\t\"                          on bar put increment() into my.innerHTML'></div>\"\n\t\t)\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait expect(find('div')).toHaveText('3')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can have multiple event handlers, no end",
    "html": "<div _='on foo put increment() into my.innerHTML                          on bar put increment() into my.innerHTML'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 1\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\t\tawait html(\n\t\t\t\"<div _='on foo put increment() into my.innerHTML\" +\n\t\t\t\"                          on bar put increment() into my.innerHTML'></div>\"\n\t\t)\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait expect(find('div')).toHaveText('3')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can ignore when target doesn't exist",
    "html": "<div _=' \ton click from #doesntExist \t\tthrow \"bar\" \ton click \t\tput \"clicked\" into me\t'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _=' \" +\n\t\t\t\"\ton click from #doesntExist \" +\n\t\t\t\"\t\tthrow \\\"bar\\\" \" +\n\t\t\t\"\ton click \" +\n\t\t\t\"\t\tput \\\"clicked\\\" into me\" +\n\t\t\t\"\t'></div>\"\n\t\t)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('clicked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can invoke on multiple events",
    "html": "<div _='on click or foo call increment()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 0\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div _='on click or foo call increment()'></div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\texpect(await evaluate(() => window._i)).toBe(1)\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")))\n\t\texpect(await evaluate(() => window._i)).toBe(2)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for attribute mutations",
    "html": "<div _='on mutation of attributes put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of attributes put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for attribute mutations on other elements",
    "html": "<div id='d1'></div><div id='d2' _='on mutation of attributes from #d1 put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1'></div>\" +\n\t\t\t\"<div id='d2' _='on mutation of attributes from #d1 put \\\"Mutated\\\" into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => document.querySelector('#work-area #d1').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('#d2')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for characterData mutation filter out other mutations",
    "html": "<div _='on mutation of characterData put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of characterData put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for childList mutation filter out other mutations",
    "html": "<div _='on mutation of childList put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of childList put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for childList mutations",
    "html": "<div _='on mutation of childList put \"Mutated\" into me then wait for hyperscript:mutation'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of childList put \\\"Mutated\\\" into me then wait for hyperscript:mutation'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').appendChild(document.createElement(\"P\")))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for events in another element (lazy)",
    "html": "<div _='on click in #d1 put it into window.tmp'>                    <div id='d1'></div>                    <div id='d2'></div>               </div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click in #d1 put it into window.tmp'>\" +\n\t\t\t\"                    <div id='d1'></div>\" +\n\t\t\t\"                    <div id='d2'></div>\" +\n\t\t\t\"               </div>\"\n\t\t)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tconst result = await evaluate(() => window.tmp === document.querySelector('#work-area #d1'))\n\t\texpect(result).toBe(true)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for general mutations",
    "html": "<div _='on mutation put \"Mutated\" into me then wait for hyperscript:mutation'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation put \\\"Mutated\\\" into me then wait for hyperscript:mutation'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for multiple mutations",
    "html": "<div _='on mutation of @foo or @bar put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of @foo or @bar put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for multiple mutations 2",
    "html": "<div _='on mutation of @foo or @bar put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of @foo or @bar put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"bar\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for specific attribute mutations",
    "html": "<div _='on mutation of @foo put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of @foo put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\tawait expect(find('div')).toHaveText('Mutated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can listen for specific attribute mutations and filter out other attribute mutations",
    "html": "<div _='on mutation of @bar put \"Mutated\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on mutation of @bar put \\\"Mutated\\\" into me'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute(\"foo\", \"bar\"))\n\t\t// Wait a bit and confirm it's still empty\n\t\tawait expect(find('div')).toHaveText('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can mix ranges",
    "html": "<div _='on click 1 put \"one\" into my.innerHTML                           on click 3 put \"three\" into my.innerHTML                           on click 2 put \"two\" into my.innerHTML '>0</div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\\'on click 1 put \"one\" into my.innerHTML ' +\n\t\t\t'                          on click 3 put \"three\" into my.innerHTML ' +\n\t\t\t'                          on click 2 put \"two\" into my.innerHTML \\'>0</div>'\n\t\t)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('one')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('two')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('three')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('three')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can pick detail fields out by name",
    "html": "<div id='d1' _='on click send custom(foo:\"fromBar\") to #d2'></div><div id='d2' _='on custom(foo) call me.classList.add(foo)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click send custom(foo:\\\"fromBar\\\") to #d2'></div>\" +\n\t\t\t\"<div id='d2' _='on custom(foo) call me.classList.add(foo)'></div>\"\n\t\t)\n\t\tawait expect(find('#d2')).not.toHaveClass(/fromBar/)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d2')).toHaveClass(/fromBar/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can pick event properties out by name",
    "html": "<div id='d1' _='on click send fromBar to #d2'></div><div id='d2' _='on fromBar(type) call me.classList.add(type)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click send fromBar to #d2'></div>\" +\n\t\t\t\"<div id='d2' _='on fromBar(type) call me.classList.add(type)'></div>\"\n\t\t)\n\t\tawait expect(find('#d2')).not.toHaveClass(/fromBar/)\n\t\tawait find('#d1').dispatchEvent('click')\n\t\tawait expect(find('#d2')).toHaveClass(/fromBar/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can queue all events",
    "html": "<div id='qa' _='on foo queue all wait for bar then call increment()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 0\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div id='qa' _='on foo queue all wait for bar then call increment()'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"foo\")))\n\t\texpect(await evaluate(() => window._i)).toBe(0)\n\n\t\tawait evaluate(() => document.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait evaluate(() => document.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"foo\")))\n\n\t\tawait evaluate(() => document.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(1)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qa').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(3)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "can queue events",
    "html": "<div id='qe' _='on foo wait for bar then call increment()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 0\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div id='qe' _='on foo wait for bar then call increment()'></div>\")\n\t\t// start first event\n\t\tawait evaluate(() => document.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"foo\")))\n\t\texpect(await evaluate(() => window._i)).toBe(0)\n\n\t\t// queue next events\n\t\tawait evaluate(() => document.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait evaluate(() => document.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"foo\")))\n\n\t\t// ungate first event handler\n\t\tawait evaluate(() => document.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(1)\n\n\t\t// Wait for queued handler to reach its \"wait for bar\" state, then dispatch bar\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\n\t\t// Wait again for next potential handler, then dispatch bar\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qe').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\t// should still be 2 - only one was queued (default queue behavior)\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "can queue first event",
    "html": "<div id='qf' _='on foo queue first wait for bar then call increment()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 0\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div id='qf' _='on foo queue first wait for bar then call increment()'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"foo\")))\n\t\texpect(await evaluate(() => window._i)).toBe(0)\n\n\t\tawait evaluate(() => document.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait evaluate(() => document.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"foo\")))\n\n\t\tawait evaluate(() => document.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(1)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #qf').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "can queue last event",
    "html": "<div id='ql' _='on foo queue last wait for bar then call increment()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 0\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div id='ql' _='on foo queue last wait for bar then call increment()'></div>\")\n\t\tawait evaluate(() => document.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"foo\")))\n\t\texpect(await evaluate(() => window._i)).toBe(0)\n\n\t\tawait evaluate(() => document.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"foo\")))\n\t\tawait evaluate(() => document.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"foo\")))\n\n\t\tawait evaluate(() => document.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"bar\")))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(1)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\n\t\tawait evaluate(() => new Promise(resolve => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.querySelector('#work-area #ql').dispatchEvent(new CustomEvent(\"bar\"))\n\t\t\t\tresolve()\n\t\t\t}, 20)\n\t\t}))\n\t\tawait expect.poll(() => evaluate(() => window._i)).toBe(2)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "can refer to event detail properties directly in filter",
    "html": "<div id='fd' _='on example[foo] increment @count then put it into me'></div>",
    "body": "\n\t\tawait html(\"<div id='fd' _='on example[foo] increment @count then put it into me'></div>\")\n\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.querySelector('#work-area #fd')\n\t\t\tconst event = new Event('example')\n\t\t\tevent.detail = {\"foo\": true}\n\t\t\tdiv.dispatchEvent(event)\n\t\t})\n\t\tawait expect(find('#fd')).toHaveText('1')\n\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.querySelector('#work-area #fd')\n\t\t\tconst event = new Event('example')\n\t\t\tevent.detail = {\"foo\": false}\n\t\t\tdiv.dispatchEvent(event)\n\t\t})\n\t\tawait expect(find('#fd')).toHaveText('1')\n\n\t\tawait evaluate(() => {\n\t\t\tconst div = document.querySelector('#work-area #fd')\n\t\t\tconst event = new Event('example')\n\t\t\tevent.detail = {\"foo\": true}\n\t\t\tdiv.dispatchEvent(event)\n\t\t})\n\t\tawait expect(find('#fd')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can refer to event properties directly in filter",
    "html": "<div id='t1' _='on click[buttons==0] log event then put \"Clicked\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='t1' _='on click[buttons==0] log event then put \\\"Clicked\\\" into my.innerHTML'></div>\")\n\t\tawait find('#t1').dispatchEvent('click')\n\t\tawait expect(find('#t1')).toHaveText('Clicked')\n\n\t\tawait html(\"<div id='t2' _='on click[buttons==1] log event then put \\\"Clicked\\\" into my.innerHTML'></div>\")\n\t\tawait find('#t2').dispatchEvent('click')\n\t\tawait expect(find('#t2')).toHaveText('')\n\n\t\tawait html(\"<div id='t3' _='on click[buttons==1 and buttons==0] log event then put \\\"Clicked\\\" into my.innerHTML'></div>\")\n\t\tawait find('#t3').dispatchEvent('click')\n\t\tawait expect(find('#t3')).toHaveText('')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can respond to events on other elements",
    "html": "<div id='bar'></div><div _='on click from #bar add .clicked'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='bar'></div>\" +\n\t\t\t\"<div _='on click from #bar add .clicked'></div>\"\n\t\t)\n\t\tawait expect(find('div').nth(1)).not.toHaveClass(/clicked/)\n\t\tawait find('#bar').dispatchEvent('click')\n\t\tawait expect(find('div').nth(1)).toHaveClass(/clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can respond to events with colons in names",
    "html": "<div _='on click send example:event to #d1'></div><div id='d1' _='on example:event add .called'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click send example:event to #d1'></div>\" +\n\t\t\t\"<div id='d1' _='on example:event add .called'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can respond to events with dots in names",
    "html": "<div _='on click send example.event to #d1'></div><div id='d1' _='on example.event add .called'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click send example.event to #d1'></div>\" +\n\t\t\t\"<div id='d1' _='on example.event add .called'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "can respond to events with minus in names",
    "html": "<div _='on click send \"a-b\" to #d1'></div><div id='d1' _='on \"a-b\" add .called'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click send \\\"a-b\\\" to #d1'></div>\" +\n\t\t\t\"<div id='d1' _='on \\\"a-b\\\" add .called'></div>\"\n\t\t)\n\t\tawait expect(find('#d1')).not.toHaveClass(/called/)\n\t\tawait find('div').first().dispatchEvent('click')\n\t\tawait expect(find('#d1')).toHaveClass(/called/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "caught exceptions do not trigger 'exception' event",
    "html": "<button _='on click put \"foo\" into me then throw \"bar\"                     catch e log e                  on exception(error) put error into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click put \\\"foo\\\" into me then throw \\\"bar\\\"\" +\n\t\t\t\"                     catch e log e\" +\n\t\t\t\"                  on exception(error) put error into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('foo')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "debounced at <time> collapses rapid events to the last one",
    "html": "<div id='d' _='on click debounced at 80ms    increment @n then put @n into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on click debounced at 80ms \" +\n\t\t\t\"   increment @n then put @n into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tconst el = document.querySelector('#work-area #d')\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t})\n\t\t// Still empty immediately after - debounce is pending\n\t\tawait expect(find('#d')).toHaveText('')\n\t\t// After the debounce window the handler runs exactly once\n\t\tawait expect(find('#d')).toHaveText('1')\n\t\t// And never progresses past 1\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 100)))\n\t\tawait expect(find('#d')).toHaveText('1')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "debounced at <time> resets the timer on each event",
    "html": "<div id='d' _='on click debounced at 80ms    increment @n then put @n into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on click debounced at 80ms \" +\n\t\t\t\"   increment @n then put @n into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => new Promise(r => {\n\t\t\tconst el = document.querySelector('#work-area #d')\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t\tsetTimeout(() => { el.dispatchEvent(new Event('click')); r() }, 40)\n\t\t}))\n\t\t// 40ms in - still pending because second click reset the timer\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\t\tawait expect(find('#d')).toHaveText('')\n\t\t// Eventually fires exactly once\n\t\tawait expect(find('#d')).toHaveText('1')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "each behavior installation has its own event queue",
    "html": "<script type=text/hyperscript>behavior DemoBehavior on foo wait 10ms then set my innerHTML to 'behavior'</script><div id='bd1' _='install DemoBehavior'></div><div id='bd2' _='install DemoBehavior'></div><div id='bd3' _='install DemoBehavior'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type=text/hyperscript>\" +\n\t\t\t\"behavior DemoBehavior on foo wait 10ms then set my innerHTML to 'behavior'\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='bd1' _='install DemoBehavior'></div>\" +\n\t\t\t\"<div id='bd2' _='install DemoBehavior'></div>\" +\n\t\t\t\"<div id='bd3' _='install DemoBehavior'></div>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area #bd1').dispatchEvent(new CustomEvent(\"foo\"))\n\t\t\tdocument.querySelector('#work-area #bd2').dispatchEvent(new CustomEvent(\"foo\"))\n\t\t\tdocument.querySelector('#work-area #bd3').dispatchEvent(new CustomEvent(\"foo\"))\n\t\t})\n\t\tawait expect(find('#bd1')).toHaveText('behavior')\n\t\tawait expect(find('#bd2')).toHaveText('behavior')\n\t\tawait expect(find('#bd3')).toHaveText('behavior')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "exceptions in catch block don't kill the event queue",
    "html": "<button _='on click                     increment :x                      if :x is 1                       throw \"bar\"                     otherwise                       put \"success\" into me                    end                   catch e                       put e into me then throw e'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click \" +\n\t\t\t\"                    increment :x  \" +\n\t\t\t\"                    if :x is 1 \" +\n\t\t\t\"                      throw \\\"bar\\\" \" +\n\t\t\t\"                    otherwise \" +\n\t\t\t\"                      put \\\"success\\\" into me\" +\n\t\t\t\"                    end \" +\n\t\t\t\"                  catch e \" +\n\t\t\t\"                      put e into me then throw e'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('success')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "exceptions in finally block don't kill the event queue",
    "html": "<button _='on click                     increment :x                    finally                      if :x is 1                       throw \"bar\"                     otherwise                       put \"success\" into me                    end                   '></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click \" +\n\t\t\t\"                    increment :x  \" +\n\t\t\t\"                  finally  \" +\n\t\t\t\"                    if :x is 1 \" +\n\t\t\t\"                      throw \\\"bar\\\" \" +\n\t\t\t\"                    otherwise \" +\n\t\t\t\"                      put \\\"success\\\" into me\" +\n\t\t\t\"                    end \" +\n\t\t\t\"                  '></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('success')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "finally blocks work when exception thrown in catch",
    "html": "<button _='on click throw \"bar\"            catch e throw e            finally put \"bar\" into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click throw \\\"bar\\\"\" +\n\t\t\t\"            catch e throw e\" +\n\t\t\t\"            finally put \\\"bar\\\" into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "halt the event stops propagation to ancestors",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <button id='inner' _='on click halt the event'></button></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <button id='inner' _='on click halt the event'></button>\" +\n\t\t\t\"</div>\"\n\t\t)\n\t\tawait find('#inner').dispatchEvent('click')\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "halt the event's bubbling stops propagation",
    "html": "<div id='outer' _='on click add .outer-clicked'>  <button id='inner' _=\"on click halt the event's bubbling\"></button></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='outer' _='on click add .outer-clicked'>\" +\n\t\t\t\"  <button id='inner' _=\\\"on click halt the event's bubbling\\\"></button>\" +\n\t\t\t\"</div>\"\n\t\t)\n\t\tawait find('#inner').dispatchEvent('click')\n\t\tawait expect(find('#outer')).not.toHaveClass(/outer-clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "handles custom events with null detail",
    "html": "<div id='d1' _='on myEvent(foo) if foo put foo into me else put \"no-detail\" into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on myEvent(foo) if foo put foo into me else put \\\"no-detail\\\" into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#work-area #d1').dispatchEvent(new CustomEvent(\"myEvent\"))\n\t\t})\n\t\tawait expect(find('#d1')).toHaveText('no-detail')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "listeners on other elements are removed when the registering element is removed",
    "html": "<div id='bar'></div><div id='listener' _='on click from #bar set #bar.innerHTML to #bar.innerHTML + \"a\"'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='bar'></div>\" +\n\t\t\t\"<div id='listener' _='on click from #bar set #bar.innerHTML to #bar.innerHTML + \\\"a\\\"'></div>\"\n\t\t)\n\t\tawait expect(find('#bar')).toHaveText('')\n\t\tawait find('#bar').dispatchEvent('click')\n\t\tawait expect(find('#bar')).toHaveText('a')\n\t\tawait evaluate(() => {\n\t\t\tconst listener = document.querySelector('#work-area #listener')\n\t\t\tlistener.parentElement.removeChild(listener)\n\t\t})\n\t\tawait find('#bar').dispatchEvent('click')\n\t\tawait expect(find('#bar')).toHaveText('a')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "listeners on self are not removed when the element is removed",
    "html": "<div id='selftest' _='on someCustomEvent put 1 into me'></div>",
    "body": "\n\t\tawait html(\"<div id='selftest' _='on someCustomEvent put 1 into me'></div>\")\n\t\tconst result = await evaluate(() => {\n\t\t\tconst div = document.querySelector('#work-area #selftest')\n\t\t\tdiv.remove()\n\t\t\tdiv.dispatchEvent(new Event(\"someCustomEvent\"))\n\t\t\treturn div.innerHTML\n\t\t})\n\t\texpect(result).toBe('1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "multiple event handlers at a time are allowed to execute with the every keyword",
    "html": "<div _='on every click put increment() into my.innerHTML then wait for a customEvent'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 1\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\t\tawait html(\"<div _='on every click put increment() into my.innerHTML then wait for a customEvent'></div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('3')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "on first click fires only once",
    "html": "<div _='on first click put 1 + my.innerHTML as Int into my.innerHTML'>0</div>",
    "body": "\n\t\tawait html(\"<div _='on first click put 1 + my.innerHTML as Int into my.innerHTML'>0</div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "on intersection fires when the element is in the viewport",
    "html": "<div id='d' _='on intersection(intersecting)    if intersecting put \"seen\" into me end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on intersection(intersecting) \" +\n\t\t\t\"   if intersecting put \\\"seen\\\" into me end'></div>\"\n\t\t)\n\t\t// IntersectionObserver fires an initial callback asynchronously;\n\t\t// work-area is in the viewport so intersecting should be true.\n\t\tawait expect(find('#d')).toHaveText('seen')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "on intersection having margin parses and fires",
    "html": "<div id='d' style='width:100px;height:100px'   _='on intersection(intersecting) having margin \"10px\"        if intersecting put \"seen\" into me end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' style='width:100px;height:100px' \" +\n\t\t\t\"  _='on intersection(intersecting) having margin \\\"10px\\\" \" +\n\t\t\t\"       if intersecting put \\\"seen\\\" into me end'></div>\"\n\t\t)\n\t\tawait expect(find('#d')).toHaveText('seen')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "on intersection having threshold parses and fires",
    "html": "<div id='d' style='width:100px;height:100px'   _='on intersection(intersecting) having threshold 0.1        if intersecting put \"seen\" into me end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' style='width:100px;height:100px' \" +\n\t\t\t\"  _='on intersection(intersecting) having threshold 0.1 \" +\n\t\t\t\"       if intersecting put \\\"seen\\\" into me end'></div>\"\n\t\t)\n\t\tawait expect(find('#d')).toHaveText('seen')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "on load from window responds to window load events",
    "html": "<div id='d' _='on customload from window put \"loaded\" into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on customload from window put \\\"loaded\\\" into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => window.dispatchEvent(new Event('customload')))\n\t\tawait expect(find('#d')).toHaveText('loaded')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "queue none does not allow future queued events",
    "html": "<div _='on click queue none put increment() into my.innerHTML then wait for a customEvent'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow._i = 1\n\t\t\twindow.increment = function() { return window._i++ }\n\t\t})\n\n\t\tawait html(\"<div _='on click queue none put increment() into my.innerHTML then wait for a customEvent'></div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"customEvent\")))\n\t\t// After ungating, the next click should not have been queued\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "rethrown exceptions trigger 'exception' event",
    "html": "<button _='on click put \"foo\" into me then throw \"bar\"                     catch e throw e                  on exception(error) put error into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click put \\\"foo\\\" into me then throw \\\"bar\\\"\" +\n\t\t\t\"                     catch e throw e\" +\n\t\t\t\"                  on exception(error) put error into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "supports \"elsewhere\" modifier",
    "html": "<div _='on click elsewhere add .clicked'></div>",
    "body": "\n\t\tawait html(\"<div _='on click elsewhere add .clicked'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/clicked/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/clicked/)\n\t\tawait evaluate(() => document.body.click())\n\t\tawait expect(find('div')).toHaveClass(/clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "supports \"from elsewhere\" modifier",
    "html": "<div _='on click from elsewhere add .clicked'></div>",
    "body": "\n\t\tawait html(\"<div _='on click from elsewhere add .clicked'></div>\")\n\t\tawait expect(find('div')).not.toHaveClass(/clicked/)\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).not.toHaveClass(/clicked/)\n\t\tawait evaluate(() => document.body.click())\n\t\tawait expect(find('div')).toHaveClass(/clicked/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "on",
    "name": "throttled at <time> allows events after the window elapses",
    "html": "<div id='d' _='on click throttled at 60ms    increment @n then put @n into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on click throttled at 60ms \" +\n\t\t\t\"   increment @n then put @n into me'></div>\"\n\t\t)\n\t\tawait find('#d').dispatchEvent('click')\n\t\tawait expect(find('#d')).toHaveText('1')\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 80)))\n\t\tawait find('#d').dispatchEvent('click')\n\t\tawait expect(find('#d')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "throttled at <time> drops events within the window",
    "html": "<div id='d' _='on click throttled at 200ms    increment @n then put @n into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d' _='on click throttled at 200ms \" +\n\t\t\t\"   increment @n then put @n into me'></div>\"\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tconst el = document.querySelector('#work-area #d')\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t\tel.dispatchEvent(new Event('click'))\n\t\t})\n\t\t// First click runs immediately; the rest are dropped\n\t\tawait expect(find('#d')).toHaveText('1')\n\t\tawait evaluate(() => new Promise(r => setTimeout(r, 50)))\n\t\tawait expect(find('#d')).toHaveText('1')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "on",
    "name": "uncaught exceptions trigger 'exception' event",
    "html": "<button _='on click put \"foo\" into me then throw \"bar\"                  on exception(error) put error into me'></button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<button _='on click put \\\"foo\\\" into me then throw \\\"bar\\\"\" +\n\t\t\t\"                  on exception(error) put error into me'></button>\"\n\t\t)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveText('bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pick",
    "name": "can pick a single item from a string",
    "html": "",
    "body": "\n\t\tawait run(`pick item 2 from str\n\t\t\tset $test to it`, {locals: {str: \"abcdefghijklmnopqrstuvwxyz\"}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toBe(\"c\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick a single item from an array",
    "html": "",
    "body": "\n\t\tawait run(`pick item 2 from arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([12]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick a single regex match",
    "html": "",
    "body": "\n\t\tconst haystack = \"The 32 quick brown foxes jumped 12 times over the 149 lazy dogs\";\n\t\tawait run(String.raw`pick match of \"\\\\d+\" from haystack\n\t\t\tset window.test to it`, {locals: {haystack}});\n\t\tconst result = await evaluate(() => [...window.test]);\n\t\texpect(result).toEqual([\"32\"]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick a single regex match w/ a flag",
    "html": "",
    "body": "\n\t\tconst haystack = \"The 32 quick brown foxes jumped 12 times over the 149 lazy dogs\";\n\t\tawait run(String.raw`pick match of \"t.e\" | i from haystack\n\t\t\tset window.test to it`, {locals: {haystack}});\n\t\tconst result = await evaluate(() => [...window.test]);\n\t\texpect(result).toEqual([\"The\"]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick all regex matches",
    "html": "",
    "body": "\n\t\tconst haystack = \"The 32 quick brown foxes jumped 12 times over the 149 lazy dogs\";\n\t\tawait run(String.raw`pick matches of \"\\\\d+\" from haystack\n\t\t\tset window.test to it`, {locals: {haystack}});\n\t\tconst result = await evaluate(() => Array.from(window.test).map(m => Array.from(m)));\n\t\texpect(result).toEqual([[\"32\"], [\"12\"], [\"149\"]]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick first n items",
    "html": "",
    "body": "\n\t\tawait run(`pick first 3 of arr\n\t\t\tset $test to it`, {locals: {arr: [10, 20, 30, 40, 50]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([10, 20, 30]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick items from a string",
    "html": "",
    "body": "\n\t\tawait run(`pick items 1 to 3 from str\n\t\t\tset $test to it`, {locals: {str: \"abcdefghijklmnopqrstuvwxyz\"}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toBe(\"bc\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick items from an array",
    "html": "",
    "body": "\n\t\tawait run(`pick items 1 to 3 from arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([11, 12]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick items using 'of' syntax",
    "html": "",
    "body": "\n\t\tawait run(`pick items 1 to 3 of arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([11, 12]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick last n items",
    "html": "",
    "body": "\n\t\tawait run(`pick last 2 of arr\n\t\t\tset $test to it`, {locals: {arr: [10, 20, 30, 40, 50]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([40, 50]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick match using 'of' syntax",
    "html": "",
    "body": "\n\t\tconst haystack = \"The 32 quick brown foxes\";\n\t\tawait run(String.raw`pick match of \"\\\\d+\" of haystack\n\t\t\tset window.test to it`, {locals: {haystack}});\n\t\tconst result = await evaluate(() => [...window.test]);\n\t\texpect(result).toEqual([\"32\"]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick random item",
    "html": "",
    "body": "\n\t\tawait run(`pick random of arr\n\t\t\tset $test to it`, {locals: {arr: [10, 20, 30]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect([10, 20, 30]).toContain(result);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can pick random n items",
    "html": "",
    "body": "\n\t\tawait run(`pick random 2 of arr\n\t\t\tset $test to it`, {locals: {arr: [10, 20, 30, 40, 50]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toHaveLength(2);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use 'end' when picking items from a string",
    "html": "",
    "body": "\n\t\tawait run(`pick item 4 to end from str\n\t\t\tset $test to it`, {locals: {str: \"abcdefghijklmnopqrstuvwxyz\"}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toBe(\"efghijklmnopqrstuvwxyz\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use 'end' when picking items from an array",
    "html": "",
    "body": "\n\t\tawait run(`pick item 4 to end from arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([14, 15, 16]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use 'start' when picking items from a string",
    "html": "",
    "body": "\n\t\tawait run(`pick items start to 3 from str\n\t\t\tset $test to it`, {locals: {str: \"abcdefghijklmnopqrstuvwxyz\"}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toBe(\"abc\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use 'start' when picking items from an array",
    "html": "",
    "body": "\n\t\tawait run(`pick items start to 3 from arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([10, 11, 12]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use negative indices when picking items from a string",
    "html": "",
    "body": "\n\t\tawait run(`pick items 0 to -4 from str\n\t\t\tset $test to it`, {locals: {str: \"abcdefghijklmnopqrstuvwxyz\"}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toBe(\"abcdefghijklmnopqrstuv\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "can use negative indices when picking items from an array",
    "html": "",
    "body": "\n\t\tawait run(`pick items 0 to -4 from arr\n\t\t\tset $test to it`, {locals: {arr: [10, 11, 12, 13, 14, 15, 16]}});\n\t\tconst result = await evaluate(() => window.$test);\n\t\texpect(result).toEqual([10, 11, 12]);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "does not hang on zero-length regex matches",
    "html": "",
    "body": "\n\t\tawait run(String.raw`pick matches of \"\\\\d*\" from haystack\n\t\t\tset window.test to it`, {locals: {haystack: \"a1b\"}});\n\t\tconst result = await evaluate(() => Array.from(window.test).map(m => m[0]));\n\t\texpect(result).toContain(\"1\");\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "pick",
    "name": "pick first from null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(\"set x to null then pick first 3 from x then return it\");\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "pick",
    "name": "pick last from null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(\"set x to null then pick last 2 from x then return it\");\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "pick",
    "name": "pick match from null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(String.raw`set x to null then pick match of \"\\\\d+\" from x then return it`);\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "pick",
    "name": "pick random from null returns null",
    "html": "",
    "body": "\n\t\tvar result = await run(\"set x to null then pick random from x then return it\");\n\t\texpect(result).toBeNull();\n\t",
    "async": true,
    "complexity": "run-eval"
  },
  {
    "category": "pseudoCommand",
    "name": "Basic instance function with expression",
    "html": "<div id='d1' _='on click getElementById(\"d1\") from the document                                           put the result into window.results'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click getElementById(\\\"d1\\\") from the document \" +\n\t\t\t\t\"                                          put the result into window.results'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst match = await evaluate(() => window.results === document.querySelector('#d1'));\n\t\texpect(match).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Basic instance function with expression and on",
    "html": "<div id='d1' _='on click getElementById(\"d1\") on the document                                           put result into window.results'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click getElementById(\\\"d1\\\") on the document \" +\n\t\t\t\t\"                                          put result into window.results'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst match = await evaluate(() => window.results === document.querySelector('#d1'));\n\t\texpect(match).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Basic instance function with expression and with",
    "html": "<div id='d1' _='on click getElementById(\"d1\") with the document                                           put result into window.results'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click getElementById(\\\"d1\\\") with the document \" +\n\t\t\t\t\"                                          put result into window.results'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst match = await evaluate(() => window.results === document.querySelector('#d1'));\n\t\texpect(match).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Basic instance function with me target",
    "html": "<div id='d1' _='on click foo() on me                                           put result into my bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click foo() on me \" +\n\t\t\t\t\"                                          put result into my bar'></div>\"\n\t\t);\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#d1').foo = function () { return \"foo\"; };\n\t\t});\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Basic instance function with me target no preposition",
    "html": "<div id='d1' _='on click foo() me                                           put result into my bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click foo() me \" +\n\t\t\t\"                                          put result into my bar'></div>\"\n\t\t);\n\t\tawait evaluate(() => {\n\t\t\tdocument.querySelector('#d1').foo = function () { return \"foo\"; };\n\t\t});\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Can use functions defined outside of the current element",
    "html": "<div id='d1' _='on click foo() then                                          put result into my bar'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.foo = function() { return \"foo\"; };\n\t\t});\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click foo() then\" +\n\t\t\t\t\"                                          put result into my bar'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Can use indirect functions with a function root",
    "html": "<div id='d1' _='on click bar().foo() then                                          put the result into my bar'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.bar = function() {\n\t\t\t\treturn { foo: function() { return \"foo\"; } };\n\t\t\t};\n\t\t});\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click bar().foo() then\" +\n\t\t\t\"                                          put the result into my bar'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Can use indirect functions with a symbol root",
    "html": "<div id='d1' _='on click bar.foo() then                                          put the result into my bar'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.bar = { foo: function() { return \"foo\"; } };\n\t\t});\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click bar.foo() then\" +\n\t\t\t\"                                          put the result into my bar'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "Can use nested indirect functions with a symbol root",
    "html": "<div id='d1' _='on click window.bar().foo() then                                          put the result into my bar'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.bar = function() {\n\t\t\t\treturn { foo: function() { return \"foo\"; } };\n\t\t\t};\n\t\t});\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click window.bar().foo() then\" +\n\t\t\t\"                                          put the result into my bar'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "functions defined alongside can be invoked",
    "html": "<div id='d1' _='def foo() return \"foo\" end on click foo() then put result into my bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='def foo() return \\\"foo\\\" end on click foo() then put result into my bar'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst bar = await evaluate(() => document.querySelector('#d1').bar);\n\t\texpect(bar).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "pseudoCommand",
    "name": "non-function pseudo-command is an error",
    "html": "",
    "body": "\n\t\tconst msg = await error(\"on click log me then foo.bar + bar\");\n\t\texpect(msg).toMatch(/^Pseudo-commands must be function calls/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can insert after",
    "html": "<div id='d1'>foo</div><div id='d2' _='on click put #d1 after #d2'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'>foo</div><div id='d2' _='on click put #d1 after #d2'></div>\");\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tconst nextText = await evaluate(() => document.querySelector('#d2').nextSibling.textContent);\n\t\texpect(nextText).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can insert after beginning",
    "html": "<div id='d1' _='on click put \"foo\" at start of #d1'>*</div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" at start of #d1'>*</div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo*\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can insert before",
    "html": "<div id='d2' _='on click put #d1 before #d2'></div><div id='d1'>foo</div>",
    "body": "\n\t\tawait html(\"<div id='d2' _='on click put #d1 before #d2'></div><div id='d1'>foo</div>\");\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tconst prevText = await evaluate(() => document.querySelector('#d2').previousSibling.textContent);\n\t\texpect(prevText).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can insert before end",
    "html": "<div id='d1' _='on click put \"foo\" at end of #d1'>*</div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" at end of #d1'>*</div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"*foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put array vals w/ array access syntax",
    "html": "<div _='on click set arr to [1, 2, 3] put \"red\" into arr[0] put arr[0] into my *color'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set arr to [1, 2, 3] put \\\"red\\\" into arr[0] put arr[0] into my *color'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put array vals w/ array access syntax and var",
    "html": "<div _='on click set arr to [1, 2, 3] set idx to 0 put \"red\" into arr[idx] put arr[0] into my *color'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set arr to [1, 2, 3] set idx to 0 put \\\"red\\\" into arr[idx] put arr[0] into my *color'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put at end of an array",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2]\n\t\t                      put 3 at end of :arr\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2]\n\t\t                      put 3 at end of :arr\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put at start of an array",
    "html": "<div _=\"on click\n\t\t                      set :arr to [2,3]\n\t\t                      put 1 at start of :arr\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [2,3]\n\t\t                      put 1 at start of :arr\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put directly into nodes",
    "html": "<div id='d1' _='on click put \"foo\" into #d1'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" into #d1'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put directly into symbols",
    "html": "<div _='on click put \"foo\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"foo\\\" into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put nodes into nodes",
    "html": "<div id='d1'></div><div id='d2' _='on click put #d1 into #d2'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'></div><div id='d2' _='on click put #d1 into #d2'></div>\");\n\t\tawait find('#d2').dispatchEvent('click');\n\t\tconst hasChild = await evaluate(() => document.querySelector('#d2').firstChild === document.querySelector('#d1'));\n\t\texpect(hasChild).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put properties w/ array access syntax",
    "html": "<div _='on click put \"red\" into my style[\"color\"]'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"red\\\" into my style[\\\"color\\\"]'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can put properties w/ array access syntax and var",
    "html": "<div _='on click set foo to \"color\" then put \"red\" into my style[foo]'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set foo to \\\"color\\\" then put \\\"red\\\" into my style[foo]'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into attribute ref",
    "html": "<div class='divs' _='on click put \"foo\" into @bar'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click put \\\"foo\\\" into @bar'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into class ref w/ flatmapped property",
    "html": "<div _='on click put \"foo\" into .divs.parentElement.innerHTML'></div><div id='d1'><div class='divs'></div></div><div id='d2'><div class='divs'></div></div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"foo\\\" into .divs.parentElement.innerHTML'></div>\" +\n\t\t\t\"<div id='d1'><div class='divs'></div></div><div id='d2'><div class='divs'></div></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t\tawait expect(find('#d2')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into class ref w/ flatmapped property using of",
    "html": "<div _='on click put \"foo\" into innerHTML of parentElement of .divs'></div><div id='d1'><div class='divs'></div></div><div id='d2'><div class='divs'></div></div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"foo\\\" into innerHTML of parentElement of .divs'></div>\" +\n\t\t\t\"<div id='d1'><div class='divs'></div></div><div id='d2'><div class='divs'></div></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t\tawait expect(find('#d2')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into id ref",
    "html": "<div id='d1' _='on click put \"foo\" into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" into #d1.innerHTML'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect attribute ref",
    "html": "<div class='divs' _='on click put \"foo\" into my @bar'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click put \\\"foo\\\" into my @bar'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect attribute ref 2",
    "html": "<div class='divs' _=\"on click put 'foo' into #div2's @bar\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click put 'foo' into #div2's @bar\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect attribute ref 3",
    "html": "<div class='divs' _=\"on click put 'foo' into @bar of #div2\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click put 'foo' into @bar of #div2\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect style ref",
    "html": "<div class='divs' _='on click put \"red\" into my *color'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click put \\\"red\\\" into my *color'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect style ref 2",
    "html": "<div class='divs' _=\"on click put 'red' into #div2's *color\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click put 'red' into #div2's *color\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into indirect style ref 3",
    "html": "<div class='divs' _=\"on click put 'red' into the *color of #div2\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click put 'red' into the *color of #div2\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set into style ref",
    "html": "<div class='divs' _='on click put \"red\" into *color'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click put \\\"red\\\" into *color'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set javascript globals",
    "html": "<div _='on click put \"red\" into window.temp'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"red\\\" into window.temp'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.temp)).toBe(\"red\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set local variables",
    "html": "<div id='d1' _='on click put \"foo\" into newVar then                                    put newVar into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click put \\\"foo\\\" into newVar then\" +\n\t\t\t\t\"                                    put newVar into #d1.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set properties",
    "html": "<div id='d1' _='on click put \"foo\" into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"foo\\\" into #d1.innerHTML'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "can set styles",
    "html": "<div _='on click put \"red\" into my.style.color'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"red\\\" into my.style.color'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "is null tolerant",
    "html": "<div class='divs' _='on click put \"red\" into #a-bad-id-that-does-not-exist'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click put \\\"red\\\" into #a-bad-id-that-does-not-exist'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "me symbol doesn't get stomped on direct write",
    "html": "<div _='on click put \"foo\" into me then put \"bar\" into me'></div>",
    "body": "\n\t\tawait html('<div _=\\'on click put \"foo\" into me then put \"bar\" into me\\'></div>');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript after",
    "html": "<div id='d1' _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" after me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" after me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait find('#b1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript at end of",
    "html": "<div id='d1' _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" at the end of me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" at the end of me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait evaluate(() => {\n\t\t\tconst e = new Event('click', {bubbles: false});\n\t\t\tdocument.querySelector('#b1').dispatchEvent(e);\n\t\t});\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript at start of",
    "html": "<div id='d1' _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" at the start of me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" at the start of me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait evaluate(() => {\n\t\t\tconst e = new Event('click', {bubbles: false});\n\t\t\tdocument.querySelector('#b1').dispatchEvent(e);\n\t\t});\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript in before",
    "html": "<div id='d1' _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" before me'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" before me'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait find('#b1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript in new content in a element target",
    "html": "<div id='d1' _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" into <div#d1/>'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" into <div#d1/>'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait evaluate(() => {\n\t\t\tconst e = new Event('click', {bubbles: false});\n\t\t\tdocument.querySelector('#b1').dispatchEvent(e);\n\t\t});\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "properly processes hyperscript in new content in a symbol write",
    "html": "<div _='on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click put \\\"<button id=\\\\\\\"b1\\\\\\\" _=\\\\\\\"on click put 42 into me\\\\\\\">40</button>\\\" into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('#b1')).toHaveText(\"40\");\n\t\tawait evaluate(() => {\n\t\t\tconst e = new Event('click', {bubbles: false});\n\t\t\tdocument.querySelector('#b1').dispatchEvent(e);\n\t\t});\n\t\tawait expect(find('#b1')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "put null into attribute removes it",
    "html": "<div id='d1' foo='bar' _='on click put null into @foo'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' foo='bar' _='on click put null into @foo'></div>\");\n\t\tawait expect(find('#d1')).toHaveAttribute('foo', 'bar');\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveAttribute('foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "put",
    "name": "waits on promises",
    "html": "<div id='d1' _='on click put promiseAString() into #d1.innerHTML'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.promiseAString = function () {\n\t\t\t\treturn new Promise(function (finish) {\n\t\t\t\t\twindow.finish = finish;\n\t\t\t\t});\n\t\t\t};\n\t\t});\n\t\tawait html(\"<div id='d1' _='on click put promiseAString() into #d1.innerHTML'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait evaluate(() => window.finish(\"foo\"));\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "reactive-properties",
    "name": "live block tracks property reads on plain objects",
    "html": "<output _=\"live put $config's label into me\"></output>",
    "body": "\n\t\tawait run(\"set $config to {label: 'initial'}\")\n\t\tawait html(`<output _=\"live put $config's label into me\"></output>`)\n\t\tawait expect.poll(() => find('output').textContent()).toBe('initial')\n\n\t\tawait run(\"set $config's label to 'changed'\")\n\t\tawait expect.poll(() => find('output').textContent()).toBe('changed')\n\t\tawait evaluate(() => { delete window.$config })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reactive-properties",
    "name": "nested property chain triggers on intermediate reassignment",
    "html": "<output _=\"when $data's inner's val changes put it into me\"></output>",
    "body": "\n\t\tawait run(\"set $data to {inner: {val: 'hello'}}\")\n\t\tawait html(`<output _=\"when $data's inner's val changes put it into me\"></output>`)\n\t\tawait expect(find('output')).toHaveText('hello')\n\n\t\tawait run(\"set $data's inner to {val: 'world'}\")\n\t\tawait expect.poll(() => find('output').textContent()).toBe('world')\n\t\tawait evaluate(() => { delete window.$data })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reactive-properties",
    "name": "property change on DOM element triggers reactivity via setProperty",
    "html": "<input type=\"text\" id=\"prop-input\" value=\"start\" /><output _=\"when #prop-input's value changes put it into me\"></output>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"prop-input\" value=\"start\" />` +\n\t\t\t`<output _=\"when #prop-input's value changes put it into me\"></output>`\n\t\t)\n\t\tawait expect(find('output')).toHaveText('start')\n\n\t\tawait run(\"set #prop-input's value to 'updated'\")\n\t\tawait expect.poll(() => find('output').textContent()).toBe('updated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reactive-properties",
    "name": "setting a property on a plain object triggers reactivity",
    "html": "<output _=\"when ($obj's x + $obj's y) changes put it into me\"></output>",
    "body": "\n\t\tawait run(\"set $obj to {x: 1, y: 2}\")\n\t\tawait html(`<output _=\"when ($obj's x + $obj's y) changes put it into me\"></output>`)\n\t\tawait expect(find('output')).toHaveText('3')\n\n\t\tawait run(\"set $obj's x to 10\")\n\t\tawait expect.poll(() => find('output').textContent()).toBe('12')\n\t\tawait evaluate(() => { delete window.$obj })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can delete a property via 'of' form",
    "html": "<div _=\"on click\n\t\t                      set :obj to { a: 1, b: 2, c: 3 }\n\t\t                      remove b of :obj\n\t\t                      put :obj.a + ',' + (:obj.b is undefined) + ',' + :obj.c into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :obj to { a: 1, b: 2, c: 3 }\n\t\t                      remove b of :obj\n\t\t                      put :obj.a + ',' + (:obj.b is undefined) + ',' + :obj.c into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,true,3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can delete a property via dot notation",
    "html": "<div _=\"on click\n\t\t                      set :obj to { a: 1, b: 2, c: 3 }\n\t\t                      remove :obj.b\n\t\t                      put :obj.a + ',' + (:obj.b is undefined) + ',' + :obj.c into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :obj to { a: 1, b: 2, c: 3 }\n\t\t                      remove :obj.b\n\t\t                      put :obj.a + ',' + (:obj.b is undefined) + ',' + :obj.c into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,true,3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can filter class removal via the when clause",
    "html": "<div id='trigger' _='on click remove .highlight from .item when it matches .old'></div><div id='d1' class='item old highlight'></div><div id='d2' class='item highlight'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='trigger' _='on click remove .highlight from .item when it matches .old'></div>\" +\n\t\t\t\"<div id='d1' class='item old highlight'></div>\" +\n\t\t\t\"<div id='d2' class='item highlight'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\t// d1 matches .old -> remove .highlight\n\t\tawait expect(find('#d1')).not.toHaveClass(/highlight/);\n\t\t// d2 does not match .old -> reverse (add .highlight, but it already has it)\n\t\tawait expect(find('#d2')).toHaveClass(/highlight/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove CSS properties",
    "html": "<div style='color: red; font-weight: bold;' _='on click remove {color} from me'></div>",
    "body": "\n\t\tawait html(\"<div style='color: red; font-weight: bold;' _='on click remove {color} from me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tvar style = await evaluate(() => document.querySelector('#work-area div').style.color);\n\t\texpect(style).toBe('');\n\t\t// font-weight should remain\n\t\tvar fw = await evaluate(() => document.querySelector('#work-area div').style.fontWeight);\n\t\texpect(fw).toBe('bold');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove a value from a set",
    "html": "<div _=\"on click\n\t\t                      set :s to ['a','b','c'] as Set\n\t\t                      remove 'b' from :s\n\t\t                      put :s.size into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :s to ['a','b','c'] as Set\n\t\t                      remove 'b' from :s\n\t\t                      put :s.size into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove a value from an array",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove 3 from :arr\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove 3 from :arr\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,4\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove class ref on a single div",
    "html": "<div class='foo' _='on click remove .foo'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' _='on click remove .foo'></div>\");\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove class ref on a single form",
    "html": "<form class='foo' _='on click remove .foo'></form>",
    "body": "\n\t\tawait html(\"<form class='foo' _='on click remove .foo'></form>\");\n\t\tawait expect(find('form')).toHaveClass(/foo/);\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('form')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove elements",
    "html": "<div _='on click remove me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click remove me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove multiple CSS properties",
    "html": "<div style='color: red; font-weight: bold; opacity: 0.5;' _='on click remove {color; font-weight} from me'></div>",
    "body": "\n\t\tawait html(\"<div style='color: red; font-weight: bold; opacity: 0.5;' _='on click remove {color; font-weight} from me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tvar color = await evaluate(() => document.querySelector('#work-area div').style.color);\n\t\tvar fw = await evaluate(() => document.querySelector('#work-area div').style.fontWeight);\n\t\tvar op = await evaluate(() => document.querySelector('#work-area div').style.opacity);\n\t\texpect(color).toBe('');\n\t\texpect(fw).toBe('');\n\t\texpect(op).toBe('0.5');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove multiple class refs",
    "html": "<div class='foo bar doh' _='on click remove .foo .bar'></div>",
    "body": "\n\t\tawait html(\"<div class='foo bar doh' _='on click remove .foo .bar'></div>\");\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t\tawait expect(find('div')).toHaveClass(/doh/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t\tawait expect(find('div')).toHaveClass(/doh/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove non-class attributes",
    "html": "<div foo='bar' _='on click remove [@foo]'></div>",
    "body": "\n\t\tawait html(\"<div foo='bar' _='on click remove [@foo]'></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('foo', 'bar');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveAttribute('foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove other elements",
    "html": "<div _='on click remove #that'></div><div id='that'></div>",
    "body": "\n\t\tawait html(\"<div _='on click remove #that'></div><div id='that'></div>\");\n\t\tawait expect(find('#that')).toHaveCount(1);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#that')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove parent element",
    "html": "<div id='p1'><button id='b1' _=\"on click remove my.parentElement\"></button></div>",
    "body": "\n\t\tawait html(\"<div id='p1'><button id='b1' _=\\\"on click remove my.parentElement\\\"></button></div>\");\n\t\tawait expect(find('#p1')).toHaveCount(1);\n\t\tawait find('#b1').dispatchEvent('click');\n\t\tawait expect(find('#p1')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can remove query refs from specific things",
    "html": "<div><div id='d1' _='on click remove <p/> from me'><p>foo</p>bar</div><p>doh</p></div>",
    "body": "\n\t\tawait html(\"<div><div id='d1' _='on click remove <p/> from me'><p>foo</p>bar</div><p>doh</p></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst outerHTML = await evaluate(() => document.querySelector('#work-area > div').innerHTML);\n\t\texpect(outerHTML).not.toContain(\"foo\");\n\t\texpect(outerHTML).toContain(\"bar\");\n\t\texpect(outerHTML).toContain(\"doh\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can splice an array element by index",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove :arr[1]\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove :arr[1]\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,3,4\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can splice an array with a negative index",
    "html": "<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove :arr[-1]\n\t\t                      put :arr as String into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                      set :arr to [1,2,3,4]\n\t\t                      remove :arr[-1]\n\t\t                      put :arr as String into me\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1,2,3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "can target another div for class ref",
    "html": "<div class='foo' id='bar'></div><div _='on click remove .foo from #bar'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' id='bar'></div><div _='on click remove .foo from #bar'></div>\");\n\t\tawait expect(find('#bar')).toHaveClass(/foo/);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "remove",
    "name": "remove on a property whose value is a DOM node still detaches the node",
    "html": "\n\t\t\t<div id=\"parent\">\n\t\t\t\t<span id=\"child\">kept</span>\n\t\t\t</div>\n\t\t\t<button _=\"on click\n\t\t\t             set :wrapper to { el: #child }\n\t\t\t             remove :wrapper.el\">go</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<div id=\"parent\">\n\t\t\t\t<span id=\"child\">kept</span>\n\t\t\t</div>\n\t\t\t<button _=\"on click\n\t\t\t             set :wrapper to { el: #child }\n\t\t\t             remove :wrapper.el\">go</button>\n\t\t`);\n\t\tawait expect(find('#child')).toHaveCount(1);\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#child')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic for loop with null works",
    "html": "<div _='on click repeat for x in null                                    put x at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat for x in null\" +\n\t\t\t\t\"                                    put x at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic for loop works",
    "html": "<div _='on click repeat for x in [1, 2, 3]                                    put x at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat for x in [1, 2, 3]\" +\n\t\t\t\t\"                                    put x at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic in loop works",
    "html": "<div _='on click repeat in [1, 2, 3]                                    put it at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat in [1, 2, 3]\" +\n\t\t\t\t\"                                    put it at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic property for loop works",
    "html": "<div _='on click set x to {foo:1, bar:2, baz:3}                 for prop in x                    put x[prop] at end of me                 end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click set x to {foo:1, bar:2, baz:3}\" +\n\t\t\t\"                 for prop in x \" +\n\t\t\t\"                   put x[prop] at end of me\" +\n\t\t\t\"                 end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic raw for loop with null works",
    "html": "<div _='on click for x in null                                    put x at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click for x in null\" +\n\t\t\t\t\"                                    put x at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic raw for loop works",
    "html": "<div _='on click for x in [1, 2, 3]                                    put x at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click for x in [1, 2, 3]\" +\n\t\t\t\t\"                                    put x at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "basic times loop works",
    "html": "<div _='on click repeat 3 times                                    put \"a\" at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat 3 times\" +\n\t\t\t\t'                                    put \"a\" at end of me' +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"aaa\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "bottom-tested loop always runs at least once",
    "html": "<div _='on click set x to 0                  repeat                    set x to x + 1                  until true end                  put x into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click set x to 0 \" +\n\t\t\t\"                 repeat \" +\n\t\t\t\"                   set x to x + 1 \" +\n\t\t\t\"                 until true end \" +\n\t\t\t\"                 put x into me'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "bottom-tested repeat until",
    "html": "<div _='on click set x to 0                  repeat                    set x to x + 1                  until x is 3 end                  put x into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click set x to 0 \" +\n\t\t\t\"                 repeat \" +\n\t\t\t\"                   set x to x + 1 \" +\n\t\t\t\"                 until x is 3 end \" +\n\t\t\t\"                 put x into me'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "bottom-tested repeat while",
    "html": "<div _='on click set x to 0                  repeat                    set x to x + 1                  while x < 3 end                  put x into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click set x to 0 \" +\n\t\t\t\"                 repeat \" +\n\t\t\t\"                   set x to x + 1 \" +\n\t\t\t\"                 while x < 3 end \" +\n\t\t\t\"                 put x into me'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "break exits a for-in loop",
    "html": "<div _=\"on click\n\t\t\t\trepeat for x in [1, 2, 3, 4, 5]\n\t\t\t\t\tif x is 4 break end\n\t\t\t\t\tput x at end of me\n\t\t\t\tend\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\trepeat for x in [1, 2, 3, 4, 5]\n\t\t\t\t\tif x is 4 break end\n\t\t\t\t\tput x at end of me\n\t\t\t\tend\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "break exits a simple repeat loop",
    "html": "<div _=\"on click\n\t\t\t\tset x to 0\n\t\t\t\trepeat 10 times\n\t\t\t\t\tset x to x + 1\n\t\t\t\t\tif x is 3 break end\n\t\t\t\tend\n\t\t\t\tput x into me\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\tset x to 0\n\t\t\t\trepeat 10 times\n\t\t\t\t\tset x to x + 1\n\t\t\t\t\tif x is 3 break end\n\t\t\t\tend\n\t\t\t\tput x into me\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"3\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "break exits a while loop",
    "html": "<div _=\"on click\n\t\t\t\tset x to 0\n\t\t\t\trepeat while x < 100\n\t\t\t\t\tset x to x + 1\n\t\t\t\t\tif x is 5 break end\n\t\t\t\tend\n\t\t\t\tput x into me\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\tset x to 0\n\t\t\t\trepeat while x < 100\n\t\t\t\t\tset x to x + 1\n\t\t\t\t\tif x is 5 break end\n\t\t\t\tend\n\t\t\t\tput x into me\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "can nest loops",
    "html": "<script type='text/hyperscript'>  def sprayInto(elt)    for x in [1, 2, 3]      for y in [1, 2, 3]        put x * y at end of elt      end    end  end</script><div id='d1' _='on click call sprayInto(me)'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def sprayInto(elt)\" +\n\t\t\t\t\"    for x in [1, 2, 3]\" +\n\t\t\t\t\"      for y in [1, 2, 3]\" +\n\t\t\t\t\"        put x * y at end of elt\" +\n\t\t\t\t\"      end\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click call sprayInto(me)'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"123246369\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "continue skips rest of iteration in simple repeat loop",
    "html": "<div _=\"on click\n\t\t\t\trepeat for x in [1, 2, 3, 4, 5]\n\t\t\t\t\tif x is 3 continue end\n\t\t\t\t\tput x at end of me\n\t\t\t\tend\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\trepeat for x in [1, 2, 3, 4, 5]\n\t\t\t\t\tif x is 3 continue end\n\t\t\t\t\tput x at end of me\n\t\t\t\tend\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"1245\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "for loop over undefined skips without error",
    "html": "<div _='on click repeat for x in doesNotExist put x at end of me end then put \"done\" into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat for x in doesNotExist put x at end of me end then put \\\"done\\\" into me'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"done\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "index syntax works",
    "html": "<div _='on click repeat for x in [\"a\", \"ab\", \"abc\"] index i                                    put x + i at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\\'on click repeat for x in [\"a\", \"ab\", \"abc\"] index i' +\n\t\t\t\t\"                                    put x + i at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"a0ab1abc2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "indexed by syntax works",
    "html": "<div _='on click repeat for x in [\"a\", \"ab\", \"abc\"] indexed by i                                    put x + i at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\\'on click repeat for x in [\"a\", \"ab\", \"abc\"] indexed by i' +\n\t\t\t\t\"                                    put x + i at end of me\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"a0ab1abc2\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "loop break works",
    "html": "<div _=\"on click\n\t\t\t\trepeat 2 times\n\t\t\t\t\tfor x in ['A', 'B', 'C', 'D']\n\t\t\t\t\t\tif x is 'C'\n\t\t\t\t\t\t  break\n\t\t\t\t\t\tend\n\t\t\t\t\t\tput x at end of me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\trepeat 2 times\n\t\t\t\t\tfor x in ['A', 'B', 'C', 'D']\n\t\t\t\t\t\tif x is 'C'\n\t\t\t\t\t\t  break\n\t\t\t\t\t\tend\n\t\t\t\t\t\tput x at end of me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"ABAB\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "loop continue works",
    "html": "<div _=\"on click\n\t\t\t\trepeat 2 times\n\t\t\t\t\tfor x in ['A', 'B', 'C', 'D']\n\t\t\t\t\t\tif (x != 'D') then\n\t\t\t\t\t\t\tput 'success ' + x  + '. ' at end of me\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\tput 'FAIL!!. ' at end of me\n\t\t\t\t\t\tend\n\t\t\t\t\t\tput 'expected D. ' at end of me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"on click\n\t\t\t\trepeat 2 times\n\t\t\t\t\tfor x in ['A', 'B', 'C', 'D']\n\t\t\t\t\t\tif (x != 'D') then\n\t\t\t\t\t\t\tput 'success ' + x  + '. ' at end of me\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\tput 'FAIL!!. ' at end of me\n\t\t\t\t\t\tend\n\t\t\t\t\t\tput 'expected D. ' at end of me\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"success A. success B. success C. expected D. success A. success B. success C. expected D. \");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "only executes the init expression once",
    "html": "<script type='text/hyperscript'>  def getArray()    set window.called to (window.called or 0) + 1    return [1, 2, 3]  end</script><div id='d1' _='on click for x in getArray() put x into my.innerHTML end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def getArray()\" +\n\t\t\t\t\"    set window.called to (window.called or 0) + 1\" +\n\t\t\t\t\"    return [1, 2, 3]\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click for x in getArray() put x into my.innerHTML end'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"3\");\n\t\texpect(await evaluate(() => window.called)).toBe(1);\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "repeat forever works",
    "html": "<script type='text/hyperscript'>  def repeatForeverWithReturn()    set retVal to 0    repeat forever       set retVal to retVal + 1       if retVal == 5 then         return retVal       end    end  end</script><div id='d1' _='on click put repeatForeverWithReturn() into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def repeatForeverWithReturn()\" +\n\t\t\t\t\"    set retVal to 0\" +\n\t\t\t\t\"    repeat forever\" +\n\t\t\t\t\"       set retVal to retVal + 1\" +\n\t\t\t\t\"       if retVal == 5 then\" +\n\t\t\t\t\"         return retVal\" +\n\t\t\t\t\"       end\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click put repeatForeverWithReturn() into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "repeat forever works w/o keyword",
    "html": "<script type='text/hyperscript'>  def repeatForeverWithReturn()    set retVal to 0    repeat       set retVal to retVal + 1       if retVal == 5 then         return retVal       end    end  end</script><div id='d1' _='on click put repeatForeverWithReturn() into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def repeatForeverWithReturn()\" +\n\t\t\t\t\"    set retVal to 0\" +\n\t\t\t\t\"    repeat\" +\n\t\t\t\t\"       set retVal to retVal + 1\" +\n\t\t\t\t\"       if retVal == 5 then\" +\n\t\t\t\t\"         return retVal\" +\n\t\t\t\t\"       end\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click put repeatForeverWithReturn() into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "times loop with expression works",
    "html": "<div _='on click repeat 3 + 3 times                                    put \"a\" at end of me                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat 3 + 3 times\" +\n\t\t\t\t'                                    put \"a\" at end of me' +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"aaaaaa\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "until event keyword works",
    "html": "<div id='untilTest'></div><script type='text/hyperscript'>  def repeatUntilTest()    repeat until event click from #untilTest      wait 2ms    end    return 42  end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='untilTest'></div>\" +\n\t\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def repeatUntilTest()\" +\n\t\t\t\t\"    repeat until event click from #untilTest\" +\n\t\t\t\t\"      wait 2ms\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"    return 42\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\tawait evaluate(() => {\n\t\t\twindow._testPromise = repeatUntilTest();\n\t\t});\n\t\tawait evaluate(() => document.querySelector('#untilTest').click());\n\t\tconst value = await evaluate(() => window._testPromise);\n\t\texpect(value).toBe(42);\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "until keyword works",
    "html": "<script type='text/hyperscript'>  def repeatUntilTest()    set retVal to 0    repeat until retVal == 5       set retVal to retVal + 1    end    return retVal  end</script><div id='d1' _='on click put repeatUntilTest() into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def repeatUntilTest()\" +\n\t\t\t\t\"    set retVal to 0\" +\n\t\t\t\t\"    repeat until retVal == 5\" +\n\t\t\t\t\"       set retVal to retVal + 1\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"    return retVal\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click put repeatUntilTest() into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "repeat",
    "name": "waiting in for loop works",
    "html": "<div _='on click repeat for x in [1, 2, 3]\n                                    log me                                     put x at end of me\n                                    wait 1ms\n                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click repeat for x in [1, 2, 3]\\n\" +\n\t\t\t\t\"                                    log me \" +\n\t\t\t\t\"                                    put x at end of me\\n\" +\n\t\t\t\t\"                                    wait 1ms\\n\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "waiting in raw for loop works",
    "html": "<div _='on click for x in [1, 2, 3]\n                                    put x at end of me\n                                    wait 1ms\n                                  end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click for x in [1, 2, 3]\\n\" +\n\t\t\t\t\"                                    put x at end of me\\n\" +\n\t\t\t\t\"                                    wait 1ms\\n\" +\n\t\t\t\t\"                                  end'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"123\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "where clause can use the for loop variable name",
    "html": "<div _=\"on click\n\t\t                       set :items to [{name:'a', val:5}, {name:'b', val:15}, {name:'c', val:25}]\n\t\t                       repeat for x in :items where x.val > 10\n\t\t                         put x.name at end of me\n\t\t                       end\"></div>",
    "body": "\n\t\tawait html(`<div _=\"on click\n\t\t                       set :items to [{name:'a', val:5}, {name:'b', val:15}, {name:'c', val:25}]\n\t\t                       repeat for x in :items where x.val > 10\n\t\t                         put x.name at end of me\n\t\t                       end\"></div>`);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"bc\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "repeat",
    "name": "while keyword works",
    "html": "<script type='text/hyperscript'>  def repeatWhileTest()    set retVal to 0    repeat while retVal < 5       set retVal to retVal + 1    end    return retVal  end</script><div id='d1' _='on click put repeatWhileTest() into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"  def repeatWhileTest()\" +\n\t\t\t\t\"    set retVal to 0\" +\n\t\t\t\t\"    repeat while retVal < 5\" +\n\t\t\t\t\"       set retVal to retVal + 1\" +\n\t\t\t\t\"    end\" +\n\t\t\t\t\"    return retVal\" +\n\t\t\t\t\"  end\" +\n\t\t\t\t\"</script>\" +\n\t\t\t\t\"<div id='d1' _='on click put repeatWhileTest() into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"5\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "reset",
    "name": "can reset a checkbox",
    "html": "\n\t\t\t<input type=\"checkbox\" id=\"cb1\" checked />\n\t\t\t<button _=\"on click reset #cb1\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"checkbox\" id=\"cb1\" checked />\n\t\t\t<button _=\"on click reset #cb1\">Reset</button>\n\t\t`);\n\t\tawait find('#cb1').uncheck();\n\t\tawait expect(find('#cb1')).not.toBeChecked();\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#cb1')).toBeChecked();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset a form",
    "html": "\n\t\t\t<form id=\"f1\">\n\t\t\t\t<input type=\"text\" id=\"t1\" value=\"original\" />\n\t\t\t\t<button type=\"button\" _=\"on click set #t1's value to 'changed'\">Change</button>\n\t\t\t\t<button type=\"button\" id=\"rst\" _=\"on click reset #f1\">Reset</button>\n\t\t\t</form>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<form id=\"f1\">\n\t\t\t\t<input type=\"text\" id=\"t1\" value=\"original\" />\n\t\t\t\t<button type=\"button\" _=\"on click set #t1's value to 'changed'\">Change</button>\n\t\t\t\t<button type=\"button\" id=\"rst\" _=\"on click reset #f1\">Reset</button>\n\t\t\t</form>\n\t\t`);\n\t\tawait find('#t1').fill('changed');\n\t\tawait expect(find('#t1')).toHaveValue('changed');\n\t\tawait find('#rst').dispatchEvent('click');\n\t\tawait expect(find('#t1')).toHaveValue('original');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset a select",
    "html": "\n\t\t\t<select id=\"sel1\">\n\t\t\t\t<option value=\"a\">A</option>\n\t\t\t\t<option value=\"b\" selected>B</option>\n\t\t\t\t<option value=\"c\">C</option>\n\t\t\t</select>\n\t\t\t<button _=\"on click reset #sel1\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<select id=\"sel1\">\n\t\t\t\t<option value=\"a\">A</option>\n\t\t\t\t<option value=\"b\" selected>B</option>\n\t\t\t\t<option value=\"c\">C</option>\n\t\t\t</select>\n\t\t\t<button _=\"on click reset #sel1\">Reset</button>\n\t\t`);\n\t\tawait find('#sel1').selectOption('c');\n\t\tawait expect(find('#sel1')).toHaveValue('c');\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#sel1')).toHaveValue('b');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset a text input to defaultValue",
    "html": "\n\t\t\t<input type=\"text\" id=\"t3\" value=\"hello\" />\n\t\t\t<button _=\"on click reset #t3\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"text\" id=\"t3\" value=\"hello\" />\n\t\t\t<button _=\"on click reset #t3\">Reset</button>\n\t\t`);\n\t\tawait find('#t3').fill('goodbye');\n\t\tawait expect(find('#t3')).toHaveValue('goodbye');\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#t3')).toHaveValue('hello');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset a textarea",
    "html": "\n\t\t\t<textarea id=\"ta1\">original text</textarea>\n\t\t\t<button _=\"on click reset #ta1\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<textarea id=\"ta1\">original text</textarea>\n\t\t\t<button _=\"on click reset #ta1\">Reset</button>\n\t\t`);\n\t\tawait find('#ta1').fill('new text');\n\t\tawait expect(find('#ta1')).toHaveValue('new text');\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#ta1')).toHaveValue('original text');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset an unchecked checkbox",
    "html": "\n\t\t\t<input type=\"checkbox\" id=\"cb2\" />\n\t\t\t<button _=\"on click reset #cb2\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"checkbox\" id=\"cb2\" />\n\t\t\t<button _=\"on click reset #cb2\">Reset</button>\n\t\t`);\n\t\tawait find('#cb2').check();\n\t\tawait expect(find('#cb2')).toBeChecked();\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('#cb2')).not.toBeChecked();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "can reset multiple inputs",
    "html": "\n\t\t\t<input type=\"text\" class=\"resettable\" value=\"one\" />\n\t\t\t<input type=\"text\" class=\"resettable\" value=\"two\" />\n\t\t\t<button _=\"on click reset .resettable\">Reset</button>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<input type=\"text\" class=\"resettable\" value=\"one\" />\n\t\t\t<input type=\"text\" class=\"resettable\" value=\"two\" />\n\t\t\t<button _=\"on click reset .resettable\">Reset</button>\n\t\t`);\n\t\tawait find('.resettable').first().fill('changed1');\n\t\tawait find('.resettable').last().fill('changed2');\n\t\tawait find('button').dispatchEvent('click');\n\t\tawait expect(find('.resettable').first()).toHaveValue('one');\n\t\tawait expect(find('.resettable').last()).toHaveValue('two');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "reset",
    "name": "reset with no target resets me (form)",
    "html": "\n\t\t\t<form _=\"on custom reset\">\n\t\t\t\t<input type=\"text\" id=\"t2\" value=\"default\" />\n\t\t\t</form>\n\t\t",
    "body": "\n\t\tawait html(`\n\t\t\t<form _=\"on custom reset\">\n\t\t\t\t<input type=\"text\" id=\"t2\" value=\"default\" />\n\t\t\t</form>\n\t\t`);\n\t\tawait find('#t2').fill('modified');\n\t\tawait expect(find('#t2')).toHaveValue('modified');\n\t\tawait find('form').dispatchEvent('custom');\n\t\tawait expect(find('#t2')).toHaveValue('default');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "resize",
    "name": "fires when element is resized",
    "html": "<div id='box' style='width:100px; height:100px' _='on resize put detail.width into #out'></div><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='width:100px; height:100px' \" +\n\t\t\t\"_='on resize put detail.width into #out'></div>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\tawait page.evaluate(() => {\n\t\t\tdocument.getElementById('box').style.width = '200px';\n\t\t});\n\t\tawait expect(find('#out')).toHaveText(\"200\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "resize",
    "name": "provides height in detail",
    "html": "<div id='box' style='width:100px; height:100px' _='on resize put detail.height into #out'></div><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='width:100px; height:100px' \" +\n\t\t\t\"_='on resize put detail.height into #out'></div>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\tawait page.evaluate(() => {\n\t\t\tdocument.getElementById('box').style.height = '300px';\n\t\t});\n\t\tawait expect(find('#out')).toHaveText(\"300\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "resize",
    "name": "works with from clause",
    "html": "<div id='box' style='width:100px; height:100px'></div><div id='out' _='on resize from #box put detail.width into me'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='width:100px; height:100px'></div>\" +\n\t\t\t\"<div id='out' _='on resize from #box put detail.width into me'></div>\"\n\t\t);\n\t\tawait page.evaluate(() => {\n\t\t\tdocument.getElementById('box').style.width = '150px';\n\t\t});\n\t\tawait expect(find('#out')).toHaveText(\"150\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll by without direction (defaults to down)",
    "html": "<div style='height: 5000px'></div><div _='on click scroll by 200px'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 5000px'></div>\" +\n\t\t\t\"<div _='on click scroll by 200px'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tvar scrollY = await evaluate(() => window.scrollY || document.documentElement.scrollTop);\n\t\texpect(scrollY).toBeGreaterThanOrEqual(190);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll container by amount",
    "html": "<div id='box' style='height: 100px; overflow: auto'>  <div style='height: 1000px'>tall</div></div><button id='go' _='on click scroll #box down by 200px'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='height: 100px; overflow: auto'>\" +\n\t\t\t\"  <div style='height: 1000px'>tall</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button id='go' _='on click scroll #box down by 200px'>go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar scrollTop = await evaluate(() => document.querySelector('#box').scrollTop);\n\t\texpect(scrollTop).toBeGreaterThanOrEqual(190);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll down by amount",
    "html": "<div style='height: 5000px'></div><div _='on click scroll down by 300px'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 5000px'></div>\" +\n\t\t\t\"<div _='on click scroll down by 300px'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tvar scrollY = await evaluate(() => window.scrollY || document.documentElement.scrollTop);\n\t\texpect(scrollY).toBeGreaterThanOrEqual(290);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll left by amount",
    "html": "<div id='box' style='width: 100px; overflow: auto; white-space: nowrap'>  <div style='width: 5000px; height: 50px'>wide</div></div><button id='go' _='on click scroll #box right by 300px'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='width: 100px; overflow: auto; white-space: nowrap'>\" +\n\t\t\t\"  <div style='width: 5000px; height: 50px'>wide</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button id='go' _='on click scroll #box right by 300px'>go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar scrollLeft = await evaluate(() => document.querySelector('#box').scrollLeft);\n\t\texpect(scrollLeft).toBeGreaterThanOrEqual(290);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll to an element",
    "html": "<div style='height: 2000px'></div><div id='target'>Target</div><div _='on click scroll to #target'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 2000px'></div>\" +\n\t\t\t\"<div id='target'>Target</div>\" +\n\t\t\t\"<div _='on click scroll to #target'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(3)').dispatchEvent('click');\n\t\tvar inView = await evaluate(() => {\n\t\t\tvar rect = document.querySelector('#target').getBoundingClientRect();\n\t\t\treturn rect.top >= 0 && rect.top < window.innerHeight;\n\t\t});\n\t\texpect(inView).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll to element in container",
    "html": "<div id='box' style='height: 100px; overflow: auto'>  <div style='height: 500px'>spacer</div>  <div id='item'>target</div></div><button id='go' _='on click scroll to #item in #box'>go</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='box' style='height: 100px; overflow: auto'>\" +\n\t\t\t\"  <div style='height: 500px'>spacer</div>\" +\n\t\t\t\"  <div id='item'>target</div>\" +\n\t\t\t\"</div>\" +\n\t\t\t\"<button id='go' _='on click scroll to #item in #box'>go</button>\"\n\t\t);\n\t\tawait find('#go').dispatchEvent('click');\n\t\tvar scrollTop = await evaluate(() => document.querySelector('#box').scrollTop);\n\t\texpect(scrollTop).toBeGreaterThanOrEqual(400);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll to top of element",
    "html": "<div style='height: 2000px'></div><div id='target' style='height: 200px'>Target</div><div _='on click scroll to the top of #target'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 2000px'></div>\" +\n\t\t\t\"<div id='target' style='height: 200px'>Target</div>\" +\n\t\t\t\"<div _='on click scroll to the top of #target'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(3)').dispatchEvent('click');\n\t\tvar inView = await evaluate(() => {\n\t\t\tvar rect = document.querySelector('#target').getBoundingClientRect();\n\t\t\treturn rect.top >= 0 && rect.top < window.innerHeight;\n\t\t});\n\t\texpect(inView).toBe(true);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "scroll",
    "name": "can scroll up by amount",
    "html": "<div style='height: 5000px'></div><div _='on click scroll up by 100px'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div style='height: 5000px'></div>\" +\n\t\t\t\"<div _='on click scroll up by 100px'></div>\"\n\t\t);\n\t\t// scroll down first\n\t\tawait evaluate(() => window.scrollTo(0, 500));\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tvar scrollY = await evaluate(() => window.scrollY || document.documentElement.scrollTop);\n\t\texpect(scrollY).toBeGreaterThanOrEqual(390);\n\t\texpect(scrollY).toBeLessThanOrEqual(410);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "select",
    "name": "returns selected text",
    "html": "<p id='text'>Hello World</p><button _='on click put the selection into #out'>Get</button><div id='out'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<p id='text'>Hello World</p>\" +\n\t\t\t\"<button _='on click put the selection into #out'>Get</button>\" +\n\t\t\t\"<div id='out'></div>\"\n\t\t);\n\t\t// Programmatically select \"Hello\"\n\t\tawait evaluate(() => {\n\t\t\tvar range = document.createRange();\n\t\t\tvar textNode = document.getElementById('text').firstChild;\n\t\t\trange.setStart(textNode, 0);\n\t\t\trange.setEnd(textNode, 5);\n\t\t\twindow.getSelection().removeAllRanges();\n\t\t\twindow.getSelection().addRange(range);\n\t\t});\n\t\tawait find('button').click();\n\t\tawait expect(find('#out')).toHaveText(\"Hello\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "select",
    "name": "selects implicit me",
    "html": "<input id='inp' value='test' _='on click select' />",
    "body": "\n\t\tawait html(\"<input id='inp' value='test' _='on click select' />\");\n\t\tawait find('#inp').click();\n\t\tvar selected = await evaluate(() => {\n\t\t\tvar inp = document.getElementById('inp');\n\t\t\treturn inp.value.substring(inp.selectionStart, inp.selectionEnd);\n\t\t});\n\t\texpect(selected).toBe(\"test\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "select",
    "name": "selects text in a textarea",
    "html": "<textarea id='ta'>some text</textarea><button _='on click select #ta'>Select</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<textarea id='ta'>some text</textarea>\" +\n\t\t\t\"<button _='on click select #ta'>Select</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tvar selected = await evaluate(() => {\n\t\t\tvar ta = document.getElementById('ta');\n\t\t\treturn ta.value.substring(ta.selectionStart, ta.selectionEnd);\n\t\t});\n\t\texpect(selected).toBe(\"some text\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "select",
    "name": "selects text in an input",
    "html": "<input id='inp' value='hello world' /><button _='on click select #inp'>Select</button>",
    "body": "\n\t\tawait html(\n\t\t\t\"<input id='inp' value='hello world' />\" +\n\t\t\t\"<button _='on click select #inp'>Select</button>\"\n\t\t);\n\t\tawait find('button').click();\n\t\tvar selected = await evaluate(() => {\n\t\t\tvar inp = document.getElementById('inp');\n\t\t\treturn inp.value.substring(inp.selectionStart, inp.selectionEnd);\n\t\t});\n\t\texpect(selected).toBe(\"hello world\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can reference sender in events",
    "html": "<div _='on click log 0 send foo to #bar log 3'></div><div id='bar' _='on foo add .foo-sent to sender log 1, me, sender'></div>",
    "body": "\n\t\tawait html(\"<div _='on click log 0 send foo to #bar log 3'></div><div id='bar' _='on foo add .foo-sent to sender log 1, me, sender'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events",
    "html": "<div _='on click send foo to #bar'></div><div id='bar' _='on foo add .foo-sent'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo to #bar'></div><div id='bar' _='on foo add .foo-sent'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo-sent/);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events to any expression",
    "html": "<div _='def bar return #bar on click send foo to bar()'></div><div id='bar' _='on foo add .foo-sent'></div>",
    "body": "\n\t\tawait html(\"<div _='def bar return #bar on click send foo to bar()'></div><div id='bar' _='on foo add .foo-sent'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo-sent/);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events with args",
    "html": "<div _='on click send foo(x:42) to #bar'></div><div id='bar' _='on foo put event.detail.x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo(x:42) to #bar'></div><div id='bar' _='on foo put event.detail.x into my.innerHTML'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events with colons",
    "html": "<div _='on click send foo:bar to #bar'></div><div id='bar' _='on foo:bar add .foo-sent'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo:bar to #bar'></div><div id='bar' _='on foo:bar add .foo-sent'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo-sent/);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events with colons with args",
    "html": "<div _='on click send foo:bar(x:42) to #bar'></div><div id='bar' _='on foo:bar put event.detail.x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo:bar(x:42) to #bar'></div><div id='bar' _='on foo:bar put event.detail.x into my.innerHTML'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events with dots",
    "html": "<div _='on click send foo.bar to #bar'></div><div id='bar' _='on foo.bar add .foo-sent'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo.bar to #bar'></div><div id='bar' _='on foo.bar add .foo-sent'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo-sent/);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo-sent/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "send",
    "name": "can send events with dots with args",
    "html": "<div _='on click send foo.bar(x:42) to #bar'></div><div id='bar' _='on foo.bar put event.detail.x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='on click send foo.bar(x:42) to #bar'></div><div id='bar' _='on foo.bar put event.detail.x into my.innerHTML'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can define variables with let at the element level",
    "html": "<div _='set :foo to 42 on click put :foo into my innerHTML'></div>",
    "body": "\n\t\tawait html(\"<div _='set :foo to 42 on click put :foo into my innerHTML'></div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set an attribute at the feature level",
    "html": "<div id='d1' _='set @data-foo to \"bar\"'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='set @data-foo to \\\"bar\\\"'></div>\")\n\t\tawait expect(find('#d1')).toHaveAttribute('data-foo', 'bar')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set an attribute from inside a behavior",
    "html": "<script type='text/hyperscript'>behavior MarkIt set @data-marked to 'yes' end</script><div id='d2' _='install MarkIt'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\"behavior MarkIt set @data-marked to 'yes' end\" +\n\t\t\t\"</script>\" +\n\t\t\t\"<div id='d2' _='install MarkIt'></div>\"\n\t\t)\n\t\tawait expect(find('#d2')).toHaveAttribute('data-marked', 'yes')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "set",
    "name": "can set arrays w/ array access syntax",
    "html": "<div _='on click set arr to [1, 2, 3] set arr[0] to \"red\" set my *color to arr[0]'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set arr to [1, 2, 3] set arr[0] to \\\"red\\\" set my *color to arr[0]'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set arrays w/ array access syntax and var",
    "html": "<div _='on click set arr to [1, 2, 3] set idx to 0 set arr[idx] to \"red\" set my *color to arr[0]'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set arr to [1, 2, 3] set idx to 0 set arr[idx] to \\\"red\\\" set my *color to arr[0]'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set chained indirect properties",
    "html": "<div _='on click set the innerHTML of the parentNode of #d1 to \"foo\"'><div id='d1'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click set the innerHTML of the parentNode of #d1 to \\\"foo\\\"'><div id='d1'></div></div>\"\n\t\t);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set complex indirect properties lhs",
    "html": "<div _='on click set parentNode.innerHTML of #d1 to \"foo\"'><div id='d1'></div></div>",
    "body": "\n\t\tawait html(\"<div _='on click set parentNode.innerHTML of #d1 to \\\"foo\\\"'><div id='d1'></div></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set complex indirect properties rhs",
    "html": "<div _='on click set innerHTML of #d1.parentNode to \"foo\"'><div id='d1'></div></div>",
    "body": "\n\t\tawait html(\"<div _='on click set innerHTML of #d1.parentNode to \\\"foo\\\"'><div id='d1'></div></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set indirect properties",
    "html": "<div id='d1' _='on click set innerHTML of #d1 to \"foo\"'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set innerHTML of #d1 to \\\"foo\\\"'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into attribute ref",
    "html": "<div class='divs' _='on click set @bar to \"foo\"'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click set @bar to \\\"foo\\\"'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into class ref",
    "html": "<div class='divs' _='on click set .divs.innerHTML to \"foo\"'></div><div class='divs'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click set .divs.innerHTML to \\\"foo\\\"'></div>\" +\n\t\t\t\"<div class='divs'></div>\");\n\t\tawait find('.divs').first().dispatchEvent('click');\n\t\tawait expect(find('.divs').first()).toHaveText(\"foo\");\n\t\tawait expect(find('.divs').nth(1)).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into id ref",
    "html": "<div id='d1' _='on click set #d1.innerHTML to \"foo\"'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set #d1.innerHTML to \\\"foo\\\"'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect attribute ref",
    "html": "<div class='divs' _=\"on click set #div2's @bar to 'foo'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set #div2's @bar to 'foo'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect attribute ref 2",
    "html": "<div class='divs' _=\"on click set #div2's @bar to 'foo'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set #div2's @bar to 'foo'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect attribute ref 3",
    "html": "<div class='divs' _=\"on click set @bar of #div2 to 'foo'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set @bar of #div2 to 'foo'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveAttribute('bar', 'foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect style ref",
    "html": "<div class='divs' _=\"on click set #div2's *color to 'red'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set #div2's *color to 'red'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect style ref 2",
    "html": "<div class='divs' _=\"on click set #div2's *color to 'red'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set #div2's *color to 'red'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into indirect style ref 3",
    "html": "<div class='divs' _=\"on click set *color of #div2 to 'red'\"></div><div id='div2'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _=\\\"on click set *color of #div2 to 'red'\\\"></div>\" +\n\t\t\t\"<div id='div2'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#div2')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set into style ref",
    "html": "<div class='divs' _='on click set *color to \"red\"'></div>",
    "body": "\n\t\tawait html(\"<div class='divs' _='on click set *color to \\\"red\\\"'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set javascript globals",
    "html": "<div _='on click set window.temp to \"red\"'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set window.temp to \\\"red\\\"'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await evaluate(() => window.temp)).toBe(\"red\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set local variables",
    "html": "<div id='d1' _='on click set newVar to \"foo\" then                                    put newVar into #d1.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click set newVar to \\\"foo\\\" then\" +\n\t\t\t\t\"                                    put newVar into #d1.innerHTML'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set many properties at once with object literal",
    "html": "<div _='on click set {bar: 2, baz: 3} on obj'></div>",
    "body": "\n\t\tawait evaluate(() => window.obj = {foo: 1});\n\t\tawait html(\"<div _='on click set {bar: 2, baz: 3} on obj'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tconst result = await evaluate(() => window.obj);\n\t\texpect(result).toEqual({foo: 1, bar: 2, baz: 3});\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set properties",
    "html": "<div id='d1' _='on click set #d1.innerHTML to \"foo\"'></div>",
    "body": "\n\t\tawait html(\"<div id='d1' _='on click set #d1.innerHTML to \\\"foo\\\"'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set props w/ array access syntax",
    "html": "<div _='on click set my style[\"color\"] to \"red\"'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set my style[\\\"color\\\"] to \\\"red\\\"'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set props w/ array access syntax and var",
    "html": "<div _='on click set foo to \"color\" then set my style[foo] to \"red\"'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set foo to \\\"color\\\" then set my style[foo] to \\\"red\\\"'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "can set styles",
    "html": "<div _='on click set my.style.color to \"red\"'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='on click set my.style.color to \\\"red\\\"'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('color', 'rgb(255, 0, 0)');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "global ($) variables are allowed at the feature level",
    "html": "<div _='set $globalAtFeature to 99'></div>",
    "body": "\n\t\tawait html(\"<div _='set $globalAtFeature to 99'></div>\")\n\t\texpect(await evaluate(() => window.$globalAtFeature)).toBe(99)\n\t\tawait evaluate(() => { delete window.$globalAtFeature })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "handles set url regression properly",
    "html": "<div _='on click set trackingcode to `foo`         set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf`         put pdfurl into me'>lolwat</div>",
    "body": "\n\t\tawait html(\"<div _='\" +\n\t\t\t\"on click set trackingcode to `foo`\" +\n\t\t\t\"         set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf`\" +\n\t\t\t\"         put pdfurl into me'>lolwat</div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"https://yyy.xxxxxx.com/path/out/foo.pdf\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "rejects bare (local) variables at the feature level",
    "html": "<div _='set localAtFeature to 42'></div>",
    "body": "\n\t\tvar messages = []\n\t\tpage.on('console', m => { if (m.type() === 'error') messages.push(m.text()) })\n\t\tpage.on('pageerror', e => messages.push(e.message))\n\t\tawait html(\"<div _='set localAtFeature to 42'></div>\")\n\t\texpect(messages.join('\\n')).toMatch(/cannot be locally scoped/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "set",
    "name": "set waits on promises",
    "html": "<div id='d1' _='on click set #d1.innerHTML to promiseAString()'></div>",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\twindow.promiseAString = function () {\n\t\t\t\treturn new Promise(function (finish) {\n\t\t\t\t\twindow.finish = finish;\n\t\t\t\t});\n\t\t\t};\n\t\t});\n\t\tawait html(\"<div id='d1' _='on click set #d1.innerHTML to promiseAString()'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait evaluate(() => window.finish(\"foo\"));\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "set",
    "name": "supports DOM-scoped (^) variables at the element level",
    "html": "<div _='set ^foo to 42 on click put ^foo into me'></div>",
    "body": "\n\t\t// ^foo is \"inherited\" (DOM) scope - allowed at feature level\n\t\tawait html(\"<div _='set ^foo to 42 on click put ^foo into me'></div>\")\n\t\tawait find('div').dispatchEvent('click')\n\t\tawait expect(find('div')).toHaveText('42')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "settle",
    "name": "can settle a collection of elements",
    "html": "<div class='item'></div><div class='item'></div><div id='trigger' _='on click settle <.item/> then add .done to <.item/>'></div>",
    "body": "\n\t\ttest.setTimeout(5000);\n\t\tawait html(\n\t\t\t\"<div class='item'></div><div class='item'></div>\" +\n\t\t\t\"<div id='trigger' _='on click settle <.item/> then add .done to <.item/>'></div>\"\n\t\t);\n\t\tawait find('#trigger').dispatchEvent('click');\n\t\tawait expect(find('.item').first()).toHaveClass(/done/);\n\t\tawait expect(find('.item').nth(1)).toHaveClass(/done/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "settle",
    "name": "can settle me no transition",
    "html": "<div id='d1' _='on click settle then add .foo'></div>",
    "body": "\n\t\ttest.setTimeout(5000);\n\t\tawait html(\"<div id='d1' _='on click settle then add .foo'></div>\");\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "settle",
    "name": "can settle target no transition",
    "html": "<div id='d1'></div><div _='on click settle #d1 then add .foo to #d1'></div>",
    "body": "\n\t\ttest.setTimeout(5000);\n\t\tawait html(\"<div id='d1'></div><div _='on click settle #d1 then add .foo to #d1'></div>\");\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can filter over a set of elements using the its symbol",
    "html": "<div _='on click show <p/> in me when its innerText contains \"foo\"'><p id='p1'>foo</p><p id='p2'>bar</p><p id='p3'>foo</p><p id='p4'>doh</p></div>",
    "body": "\n\t\tawait html(\"<div _='on click show <p/> in me when its innerText contains \\\"foo\\\"'>\" +\n\t\t\t\"<p id='p1'>foo</p>\" +\n\t\t\t\"<p id='p2'>bar</p>\" +\n\t\t\t\"<p id='p3'>foo</p>\" +\n\t\t\t\"<p id='p4'>doh</p>\" +\n\t\t\t\"</div>\");\n\n\t\tawait find('div').dispatchEvent('click');\n\n\t\tawait expect(find('#p1')).toBeVisible();\n\t\tawait expect(find('#p2')).toBeHidden();\n\t\tawait expect(find('#p3')).toBeVisible();\n\t\tawait expect(find('#p4')).toBeHidden();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element via the hidden attribute strategy",
    "html": "<div hidden _='on click show me with hidden'></div>",
    "body": "\n\t\tawait html(\"<div hidden _='on click show me with hidden'></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('hidden', '');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveAttribute('hidden', '');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element with custom display value",
    "html": "<div style='display:none' _='on click show me with display: flex'></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' _='on click show me with display: flex'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'flex');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element with display:block explicitly",
    "html": "<div style='display:none' _='on click show me with display'></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' _='on click show me with display'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element with inline-block display value",
    "html": "<div style='display:none' _='on click show me with display: inline-block'></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' _='on click show me with display: inline-block'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'inline-block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element with opacity style literal",
    "html": "<div style='opacity:0' _='on click show me with *opacity'></div>",
    "body": "\n\t\tawait html(\"<div style='opacity:0' _='on click show me with *opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element with opacity:1",
    "html": "<div style='opacity:0' _='on click show me with opacity'></div>",
    "body": "\n\t\tawait html(\"<div style='opacity:0' _='on click show me with opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element, with display:block by default",
    "html": "<div style='display:none' _='on click show me'></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' _='on click show me'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show element, with visibility:visible",
    "html": "<div style='visibility:hidden' _='on click show me with visibility'></div>",
    "body": "\n\t\tawait html(\"<div style='visibility:hidden' _='on click show me with visibility'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'hidden');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show form, with display:block by default",
    "html": "<form style='display:none' _='on click show me'></form>",
    "body": "\n\t\tawait html(\"<form style='display:none' _='on click show me'></form>\");\n\t\tawait expect(find('form')).toHaveCSS('display', 'none');\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('form')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show multiple elements as class with inline-block display value",
    "html": "<div _='on click show .c1 with display:inline-block'></div><div style='display: none' id='d1' class='c1'></div><div style='display: none' id='d2' class='c1'></div>",
    "body": "\n\t\tawait html(\"<div _='on click show .c1 with display:inline-block'></div>\" +\n\t\t\t\"<div style='display: none' id='d1' class='c1'></div>\" +\n\t\t\t\"<div style='display: none' id='d2' class='c1'></div>\");\n\t\tawait expect(find('#d1')).toBeHidden();\n\t\tawait expect(find('#d2')).toBeHidden();\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveCSS('display', 'inline-block');\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'inline-block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show multiple elements with inline-block display value",
    "html": "<div _='on click show <#d1, #d2/> with display: inline-block'></div><div style='display: none' id='d1'></div><div style='display: none' id='d2'></div>",
    "body": "\n\t\tawait html(\"<div _='on click show <#d1, #d2/> with display: inline-block'></div>\" +\n\t\t\t\"<div style='display: none' id='d1'></div>\" +\n\t\t\t\"<div style='display: none' id='d2'></div>\");\n\t\tawait expect(find('#d1')).toBeHidden();\n\t\tawait expect(find('#d2')).toBeHidden();\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveCSS('display', 'inline-block');\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'inline-block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can show other elements",
    "html": "<div style='display:none' class='showme'></div><div _='on click show .showme'></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' class='showme'></div><div _='on click show .showme'></div>\");\n\t\tawait expect(find('.showme')).toHaveCSS('display', 'none');\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('.showme')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can use a when clause and a with clause to show or hide an element",
    "html": "<div _='on click                               toggle .foo                               show with opacity when I match .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click \" +\n\t\t\t\"                              toggle .foo \" +\n\t\t\t\"                              show with opacity when I match .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "can use a when clause to show or hide an element",
    "html": "<div _='on click                               toggle .foo                               show when I match .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click \" +\n\t\t\t\"                              toggle .foo \" +\n\t\t\t\"                              show when I match .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "starting off with display none does not stick",
    "html": "<div style='display: none' _='on click toggle .foo show when I match .foo'></div>",
    "body": "\n\t\tawait html(\"<div style='display: none' _='on click toggle .foo show when I match .foo'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "the result after show...when is the matched elements",
    "html": "<div _=\"on click   show <p/> in me when its textContent is 'yes'   if the result is empty put 'none' into #out   else put 'some' into #out\"><p style='display:none'>yes</p><p style='display:none'>no</p><span id='out'>--</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _=\\\"on click \" +\n\t\t\t\"  show <p/> in me when its textContent is 'yes' \" +\n\t\t\t\"  if the result is empty put 'none' into #out \" +\n\t\t\t\"  else put 'some' into #out\\\">\" +\n\t\t\t\"<p style='display:none'>yes</p>\" +\n\t\t\t\"<p style='display:none'>no</p>\" +\n\t\t\t\"<span id='out'>--</span>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('#out')).toHaveText(\"some\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "show",
    "name": "the result in a when clause refers to previous command result, not element being tested",
    "html": "<div _=\"on click   get 'found'   show <span/> in me when the result is 'found'\"><span id='s1' style='display:none'>A</span><span id='s2' style='display:none'>B</span></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _=\\\"on click \" +\n\t\t\t\"  get 'found' \" +\n\t\t\t\"  show <span/> in me when the result is 'found'\\\">\" +\n\t\t\t\"<span id='s1' style='display:none'>A</span>\" +\n\t\t\t\"<span id='s2' style='display:none'>B</span>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('#s1')).toBeVisible();\n\t\tawait expect(find('#s2')).toBeVisible();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "socket",
    "name": "converts relative URL to ws:// on http pages",
    "html": "",
    "body": "\n\t\tawait page.route('http://localhost/test', async route => {\n\t\t\tawait route.fulfill({\n\t\t\t\tstatus: 200,\n\t\t\t\tcontentType: 'text/html',\n\t\t\t\tbody: '<!DOCTYPE html><html><body></body></html>',\n\t\t\t})\n\t\t})\n\t\tawait page.goto('http://localhost/test')\n\t\tawait page.addScriptTag({\n\t\t\tpath: path.join(__dirname, '../.bundle/_hyperscript.js')\n\t\t})\n\t\tawait page.waitForFunction(() => typeof _hyperscript !== 'undefined')\n\n\t\tvar result = await page.evaluate(() => {\n\t\t\tvar urls = [];\n\t\t\tvar OrigWS = window.WebSocket;\n\t\t\twindow.WebSocket = function(url) {\n\t\t\t\turls.push(url);\n\t\t\t\treturn { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} };\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script');\n\t\t\t\tscript.type = 'text/hyperscript';\n\t\t\t\tscript.textContent = 'socket RelSocket /my-ws end';\n\t\t\t\tdocument.body.appendChild(script);\n\t\t\t\t_hyperscript.processNode(script);\n\t\t\t\treturn { url: urls[0], error: null };\n\t\t\t} catch (e) {\n\t\t\t\treturn { url: null, error: e.message };\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS;\n\t\t\t}\n\t\t});\n\t\texpect(result.error).toBeNull();\n\t\texpect(result.url).toBe('ws://localhost/my-ws');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "converts relative URL to wss:// on https pages",
    "html": "",
    "body": "\n\t\tawait page.route('https://localhost/test', async route => {\n\t\t\tawait route.fulfill({\n\t\t\t\tstatus: 200,\n\t\t\t\tcontentType: 'text/html',\n\t\t\t\tbody: '<!DOCTYPE html><html><body></body></html>',\n\t\t\t})\n\t\t})\n\t\tawait page.goto('https://localhost/test')\n\t\tawait page.addScriptTag({\n\t\t\tpath: path.join(__dirname, '../.bundle/_hyperscript.js')\n\t\t})\n\t\tawait page.waitForFunction(() => typeof _hyperscript !== 'undefined')\n\n\t\tvar result = await page.evaluate(() => {\n\t\t\tvar urls = [];\n\t\t\tvar OrigWS = window.WebSocket;\n\t\t\twindow.WebSocket = function(url) {\n\t\t\t\turls.push(url);\n\t\t\t\treturn { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} };\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script');\n\t\t\t\tscript.type = 'text/hyperscript';\n\t\t\t\tscript.textContent = 'socket RelSocket /my-ws end';\n\t\t\t\tdocument.body.appendChild(script);\n\t\t\t\t_hyperscript.processNode(script);\n\t\t\t\treturn { url: urls[0], error: null };\n\t\t\t} catch (e) {\n\t\t\t\treturn { url: null, error: e.message };\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS;\n\t\t\t}\n\t\t});\n\t\texpect(result.error).toBeNull();\n\t\texpect(result.url).toBe('wss://localhost/my-ws');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "dispatchEvent sends JSON-encoded event over the socket",
    "html": "",
    "body": "\n\t\tvar payload = await evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar sent = []\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(msg) { sent.push(msg) },\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket DispatchSocket ws://localhost/ws end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\tvar evt = new CustomEvent('ping', {\n\t\t\t\t\tdetail: { n: 1, msg: 'hi', sender: 'internal', _namedArgList_: 'internal' }\n\t\t\t\t})\n\t\t\t\twindow.DispatchSocket.dispatchEvent(evt)\n\t\t\t\treturn JSON.parse(sent[0])\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t})\n\t\texpect(payload.type).toBe('ping')\n\t\texpect(payload.n).toBe(1)\n\t\texpect(payload.msg).toBe('hi')\n\t\texpect(payload.sender).toBeUndefined()\n\t\texpect(payload._namedArgList_).toBeUndefined()\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "namespaced sockets work",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket;\n\t\t\twindow.WebSocket = function(url) {\n\t\t\t\treturn { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} };\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script');\n\t\t\t\tscript.type = 'text/hyperscript';\n\t\t\t\tscript.textContent = 'socket MyApp.chat ws://localhost/ws end';\n\t\t\t\tdocument.body.appendChild(script);\n\t\t\t\t_hyperscript.processNode(script);\n\t\t\t\treturn { hasRaw: MyApp && MyApp.chat && typeof MyApp.chat.raw !== 'undefined', error: null };\n\t\t\t} catch (e) {\n\t\t\t\treturn { hasRaw: false, error: e.message };\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS;\n\t\t\t}\n\t\t});\n\t\texpect(result.error).toBeNull();\n\t\texpect(result.hasRaw).toBe(true);\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "on message as JSON handler decodes JSON payload",
    "html": "",
    "body": "\n\t\tvar fired = await page.evaluate(async () => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} }\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\tvar script = document.createElement('script')\n\t\t\tscript.type = 'text/hyperscript'\n\t\t\tscript.textContent = `\n\t\t\t\tsocket JsonSocket ws://localhost/ws\n\t\t\t\t  on message as JSON\n\t\t\t\t    set window.socketFiredJson to true\n\t\t\t`\n\t\t\tdocument.body.appendChild(script)\n\t\t\t_hyperscript.processNode(script)\n\t\t\tawait new Promise(r => setTimeout(r, 20))\n\t\t\twindow.WebSocket = OrigWS\n\t\t\tmockSocket.onmessage({ data: JSON.stringify({ name: 'Alice' }) })\n\t\t\tawait new Promise(r => setTimeout(r, 20))\n\t\t\treturn window.socketFiredJson\n\t\t})\n\t\texpect(fired).toBe(true)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "on message as JSON throws on non-JSON payload",
    "html": "",
    "body": "\n\t\tvar err = await page.evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} }\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\tvar script = document.createElement('script')\n\t\t\tscript.type = 'text/hyperscript'\n\t\t\tscript.textContent = `\n\t\t\t\tsocket StrictJsonSocket ws://localhost/ws\n\t\t\t\t  on message as JSON\n\t\t\t\t    set window.strictFired to true\n\t\t\t`\n\t\t\tdocument.body.appendChild(script)\n\t\t\t_hyperscript.processNode(script)\n\t\t\twindow.WebSocket = OrigWS\n\t\t\ttry {\n\t\t\t\tmockSocket.onmessage({ data: 'not-json' })\n\t\t\t\treturn null\n\t\t\t} catch (e) {\n\t\t\t\treturn e.message\n\t\t\t}\n\t\t})\n\t\texpect(err).toContain('Received non-JSON message')\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "on message handler fires on incoming text message",
    "html": "",
    "body": "\n\t\tvar received = await page.evaluate(async () => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} }\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\tvar script = document.createElement('script')\n\t\t\tscript.type = 'text/hyperscript'\n\t\t\tscript.textContent = `\n\t\t\t\tsocket TextSocket ws://localhost/ws\n\t\t\t\t  on message\n\t\t\t\t    set window.socketFired to true\n\t\t\t`\n\t\t\tdocument.body.appendChild(script)\n\t\t\t_hyperscript.processNode(script)\n\t\t\tawait new Promise(r => setTimeout(r, 20))\n\t\t\twindow.WebSocket = OrigWS\n\t\t\tmockSocket.onmessage({ data: 'hello socket' })\n\t\t\tawait new Promise(r => setTimeout(r, 20))\n\t\t\treturn window.socketFired\n\t\t})\n\t\texpect(received).toBe(true)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "parses socket with absolute ws:// URL",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => {\n\t\t\tvar urls = [];\n\t\t\tvar OrigWS = window.WebSocket;\n\t\t\twindow.WebSocket = function(url) {\n\t\t\t\turls.push(url);\n\t\t\t\treturn { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} };\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script');\n\t\t\t\tscript.type = 'text/hyperscript';\n\t\t\t\tscript.textContent = 'socket MySocket ws://localhost:1234/ws end';\n\t\t\t\tdocument.body.appendChild(script);\n\t\t\t\t_hyperscript.processNode(script);\n\t\t\t\treturn { url: urls[0], error: null };\n\t\t\t} catch (e) {\n\t\t\t\treturn { url: null, error: e.message };\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS;\n\t\t\t}\n\t\t});\n\t\texpect(result.error).toBeNull();\n\t\texpect(result.url).toBe('ws://localhost:1234/ws');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "rpc proxy blacklists then/catch/length/toJSON",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\twindow.WebSocket = function() {\n\t\t\t\treturn { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} }\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket BlacklistSocket ws://localhost/ws end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\t\t\t\treturn {\n\t\t\t\t\tthen: window.BlacklistSocket.rpc.then,\n\t\t\t\t\tcatch: window.BlacklistSocket.rpc.catch,\n\t\t\t\t\tlength: window.BlacklistSocket.rpc.length,\n\t\t\t\t\ttoJSON: window.BlacklistSocket.rpc.toJSON,\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t})\n\t\texpect(result.then).toBeNull()\n\t\texpect(result.catch).toBeNull()\n\t\texpect(result.length).toBeNull()\n\t\texpect(result.toJSON).toBeNull()\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "rpc proxy default timeout rejects the promise",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => new Promise((resolve) => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(){},\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket DefTOSocket ws://localhost/ws with timeout 50 end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\twindow.DefTOSocket.rpc.neverReplies()\n\t\t\t\t\t.then(function () { resolve({ kind: 'resolved' }) })\n\t\t\t\t\t.catch(function (e) { resolve({ kind: 'rejected', err: e }) })\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t}))\n\t\texpect(result.kind).toBe('rejected')\n\t\texpect(result.err).toBe('Timed out')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "rpc proxy noTimeout avoids timeout rejection",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => new Promise((resolve) => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar sent = []\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(msg) { sent.push(msg) },\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket NoTOSocket ws://localhost/ws with timeout 20 end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\tvar settled = false\n\t\t\t\tvar p = window.NoTOSocket.rpc.noTimeout.slowCall('x')\n\t\t\t\tp.then(function (v) { settled = true; resolve({ kind: 'resolved', value: v }) })\n\t\t\t\t .catch(function (e) { settled = true; resolve({ kind: 'rejected', err: e }) })\n\n\t\t\t\tsetTimeout(function () {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tvar outgoing = JSON.parse(sent[0])\n\t\t\t\t\t\tmockSocket.onmessage({ data: JSON.stringify({ iid: outgoing.iid, return: 'late' }) })\n\t\t\t\t\t}\n\t\t\t\t}, 80)\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t}))\n\t\texpect(result.kind).toBe('resolved')\n\t\texpect(result.value).toBe('late')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "rpc proxy reply with throw rejects the promise",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => new Promise((resolve) => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar sent = []\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(msg) { sent.push(msg) },\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket ErrSocket ws://localhost/ws end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\t\t\t\twindow.ErrSocket.rpc.boom().catch(function (err) { resolve(err) })\n\t\t\t\tvar outgoing = JSON.parse(sent[0])\n\t\t\t\tmockSocket.onmessage({ data: JSON.stringify({ iid: outgoing.iid, throw: 'kaboom' }) })\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t}))\n\t\texpect(result).toBe('kaboom')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "rpc proxy sends a message and resolves the reply",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => new Promise((resolve) => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar sent = []\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(msg) { sent.push(msg) },\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket RpcSocket ws://localhost/ws end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\t// Call an RPC method via the proxy\n\t\t\t\tvar p = window.RpcSocket.rpc.greet('world')\n\t\t\t\t// Parse the outgoing message to get the iid, then reply\n\t\t\t\tvar outgoing = JSON.parse(sent[0])\n\t\t\t\tmockSocket.onmessage({ data: JSON.stringify({ iid: outgoing.iid, return: 'hi world' }) })\n\t\t\t\tp.then(function (value) {\n\t\t\t\t\tresolve({ call: outgoing.function, args: outgoing.args, value: value })\n\t\t\t\t})\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t}))\n\t\texpect(result.call).toBe('greet')\n\t\texpect(result.args).toEqual(['world'])\n\t\texpect(result.value).toBe('hi world')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "rpc proxy timeout(n) rejects after a custom window",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => new Promise((resolve) => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = {\n\t\t\t\tonmessage: null,\n\t\t\t\tsend: function(){},\n\t\t\t\taddEventListener: function(){},\n\t\t\t\tclose: function(){},\n\t\t\t}\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket CustomTOSocket ws://localhost/ws with timeout 60000 end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\twindow.CustomTOSocket.rpc.timeout(50).willTimeOut()\n\t\t\t\t\t.then(function () { resolve({ kind: 'resolved' }) })\n\t\t\t\t\t.catch(function (e) { resolve({ kind: 'rejected', err: e }) })\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t}))\n\t\texpect(result.kind).toBe('rejected')\n\t\texpect(result.err).toBe('Timed out')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "socket",
    "name": "rpc reconnects after the underlying socket closes",
    "html": "",
    "body": "\n\t\tvar result = await evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar created = []\n\t\t\tvar closeHandlers = []\n\t\t\twindow.WebSocket = function() {\n\t\t\t\tvar sock = {\n\t\t\t\t\tonmessage: null,\n\t\t\t\t\tsend: function(){},\n\t\t\t\t\taddEventListener: function(type, handler) {\n\t\t\t\t\t\tif (type === 'close') closeHandlers.push(handler)\n\t\t\t\t\t},\n\t\t\t\t\tclose: function(){},\n\t\t\t\t}\n\t\t\t\tcreated.push(sock)\n\t\t\t\treturn sock\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket ReconnectSocket ws://localhost/ws end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\n\t\t\t\t// Simulate the server closing the connection\n\t\t\t\tcloseHandlers.forEach(function (h) { h({}) })\n\n\t\t\t\t// Next RPC call should create a fresh socket\n\t\t\t\twindow.ReconnectSocket.rpc.ping()\n\t\t\t\treturn { count: created.length }\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t})\n\t\texpect(result.count).toBe(2)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "socket",
    "name": "with timeout parses and uses the configured timeout",
    "html": "",
    "body": "\n\t\tvar ok = await evaluate(() => {\n\t\t\tvar OrigWS = window.WebSocket\n\t\t\tvar mockSocket = { onmessage: null, send: function(){}, addEventListener: function(){}, close: function(){} }\n\t\t\twindow.WebSocket = function() { return mockSocket }\n\t\t\ttry {\n\t\t\t\tvar script = document.createElement('script')\n\t\t\t\tscript.type = 'text/hyperscript'\n\t\t\t\tscript.textContent = 'socket TimedSocket ws://localhost/ws with timeout 1500 end'\n\t\t\t\tdocument.body.appendChild(script)\n\t\t\t\t_hyperscript.processNode(script)\n\t\t\t\treturn typeof window.TimedSocket === 'object' && typeof window.TimedSocket.rpc === 'object'\n\t\t\t} catch (e) {\n\t\t\t\treturn 'error: ' + e.message\n\t\t\t} finally {\n\t\t\t\twindow.WebSocket = OrigWS\n\t\t\t}\n\t\t})\n\t\texpect(ok).toBe(true)\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "swap",
    "name": "can swap a variable with a property",
    "html": "<div id='d1' _='on click set x to \"old\" then set #target.dataset.val to \"new\" then swap x with #target.dataset.val then put x into me'></div>\n\t\t\t<span id='target' data-val='x'></span>",
    "body": "\n\t\tawait html(`<div id='d1' _='on click set x to \"old\" then set #target.dataset.val to \"new\" then swap x with #target.dataset.val then put x into me'></div>\n\t\t\t<span id='target' data-val='x'></span>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"new\");\n\t\tawait expect(find('#target')).toHaveAttribute('data-val', 'old');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "swap",
    "name": "can swap array elements",
    "html": "<div id='d1' _='on click set arr to [1,2,3] then swap arr[0] with arr[2] then put arr as String into me'></div>",
    "body": "\n\t\tawait html(`<div id='d1' _='on click set arr to [1,2,3] then swap arr[0] with arr[2] then put arr as String into me'></div>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"3,2,1\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "swap",
    "name": "can swap two properties",
    "html": "<div id='d1' _='on click set #a.textContent to \"hello\" then set #b.textContent to \"world\" then swap #a.textContent with #b.textContent'></div>\n\t\t\t<span id='a'>x</span><span id='b'>y</span>",
    "body": "\n\t\tawait html(`<div id='d1' _='on click set #a.textContent to \"hello\" then set #b.textContent to \"world\" then swap #a.textContent with #b.textContent'></div>\n\t\t\t<span id='a'>x</span><span id='b'>y</span>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#a')).toHaveText(\"world\");\n\t\tawait expect(find('#b')).toHaveText(\"hello\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "swap",
    "name": "can swap two variables",
    "html": "<div id='d1' _='on click set x to \"a\" then set y to \"b\" then swap x with y then put x + y into me'></div>",
    "body": "\n\t\tawait html(`<div id='d1' _='on click set x to \"a\" then set y to \"b\" then swap x with y then put x + y into me'></div>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"ba\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "a parent can take a class for other elements",
    "html": "<div _='on click take .foo from .div for event.target'><div id='d1' class='div foo'></div><div id='d2' class='div'></div><div id='d3' class='div'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click take .foo from .div for event.target'>\" +\n\t\t\t\t\"<div id='d1' class='div foo'></div>\" +\n\t\t\t\t\"<div id='d2' class='div'></div>\" +\n\t\t\t\t\"<div id='d3' class='div'></div>\" +\n\t\t\t\t\"</div>\"\n\t\t);\n\t\tawait evaluate(() => document.querySelector('#d2').click());\n\t\tawait expect(find('#d1')).not.toHaveClass(/foo/);\n\t\tawait expect(find('#d2')).toHaveClass(/foo/);\n\t\tawait expect(find('#d3')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "a parent can take an attribute for other elements",
    "html": "<div _='on click take @data-foo from .div for event.target'><div id='d1' class='div' data-foo='bar'></div><div id='d2' class='div'></div><div id='d3' class='div'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click take @data-foo from .div for event.target'>\" +\n\t\t\t\t\"<div id='d1' class='div' data-foo='bar'></div>\" +\n\t\t\t\t\"<div id='d2' class='div'></div>\" +\n\t\t\t\t\"<div id='d3' class='div'></div>\" +\n\t\t\t\"</div>\"\n\t\t);\n\t\tawait evaluate(() => document.querySelector('#d2').click());\n\t\tawait expect(find('#d1')).not.toHaveAttribute('data-foo');\n\t\tawait expect(find('#d2')).toHaveAttribute('data-foo', '');\n\t\tawait expect(find('#d3')).not.toHaveAttribute('data-foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take a class and swap it with another via giving",
    "html": "<div class='item selected'></div><div class='item unselected' _='on click take .selected from .item giving .unselected'></div><div class='item unselected'></div>",
    "body": "\n\t\tawait html(\"<div class='item selected'></div><div class='item unselected' _='on click take .selected from .item giving .unselected'></div><div class='item unselected'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveClass(/unselected/);\n\t\tawait expect(find('div').first()).not.toHaveClass(/\\bselected\\b/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/\\bselected\\b/);\n\t\tawait expect(find('div').nth(1)).not.toHaveClass(/unselected/);\n\t\tawait expect(find('div').nth(2)).toHaveClass(/unselected/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take a class and swap it with another via with",
    "html": "<div class='item selected'></div><div class='item' _='on click take .selected with .unselected from .item'></div><div class='item'></div>",
    "body": "\n\t\tawait html(\"<div class='item selected'></div><div class='item' _='on click take .selected with .unselected from .item'></div><div class='item'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveClass(/\\bselected\\b/);\n\t\tawait expect(find('div').first()).toHaveClass(/unselected/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/\\bselected\\b/);\n\t\tawait expect(find('div').nth(1)).not.toHaveClass(/unselected/);\n\t\tawait expect(find('div').nth(2)).toHaveClass(/unselected/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take a class for other elements",
    "html": "<div class='div foo'></div><div class='div' _='on click take .foo from .div for #d3'></div><div id='d3' class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div foo'></div><div class='div' _='on click take .foo from .div for #d3'></div><div id='d3' class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(1)).not.toHaveClass(/foo/);\n\t\tawait expect(find('#d3')).toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take a class from other elements",
    "html": "<div class='div foo'></div><div class='div' _='on click take .foo from .div'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div foo'></div><div class='div' _='on click take .foo from .div'></div><div class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(2)).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take a class from other forms",
    "html": "<form class='div foo'></form><form class='div' _='on click take .foo from .div'></form><form class='div'></form>",
    "body": "\n\t\tawait html(\"<form class='div foo'></form><form class='div' _='on click take .foo from .div'></form><form class='div'></form>\");\n\t\tawait find('form').nth(1).dispatchEvent('click');\n\t\tawait expect(find('form').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('form').nth(1)).toHaveClass(/foo/);\n\t\tawait expect(find('form').nth(2)).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take an attribute for other elements",
    "html": "<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo from .div for #d3'></div><div id='d3' class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo from .div for #d3'></div><div id='d3' class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveAttribute('data-foo');\n\t\tawait expect(find('div').nth(1)).not.toHaveAttribute('data-foo');\n\t\tawait expect(find('#d3')).toHaveAttribute('data-foo', '');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take an attribute from other elements",
    "html": "<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo from .div'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo from .div'></div><div class='div'></div>\");\n\t\tawait expect(find('div').first()).toHaveAttribute('data-foo', 'bar');\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveAttribute('data-foo');\n\t\tawait expect(find('div').nth(1)).toHaveAttribute('data-foo', '');\n\t\tawait expect(find('div').nth(2)).not.toHaveAttribute('data-foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take an attribute value from other elements and set specific values instead",
    "html": "<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz with \"qux\" from .div'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz with \\\"qux\\\" from .div'></div><div class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveAttribute('data-foo', 'qux');\n\t\tawait expect(find('div').nth(1)).toHaveAttribute('data-foo', 'baz');\n\t\tawait expect(find('div').nth(2)).toHaveAttribute('data-foo', 'qux');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take an attribute value from other elements and set value from an expression instead",
    "html": "<div class='div' data-foo='bar'></div><div class='div' data-foo='qux' _='on click take @data-foo=baz with my @data-foo from .div'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' data-foo='qux' _='on click take @data-foo=baz with my @data-foo from .div'></div><div class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveAttribute('data-foo', 'qux');\n\t\tawait expect(find('div').nth(1)).toHaveAttribute('data-foo', 'baz');\n\t\tawait expect(find('div').nth(2)).toHaveAttribute('data-foo', 'qux');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take an attribute with specific value from other elements",
    "html": "<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz from .div'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz from .div'></div><div class='div'></div>\");\n\t\tawait expect(find('div').first()).toHaveAttribute('data-foo', 'bar');\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveAttribute('data-foo');\n\t\tawait expect(find('div').nth(1)).toHaveAttribute('data-foo', 'baz');\n\t\tawait expect(find('div').nth(2)).not.toHaveAttribute('data-foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take multiple classes from other elements",
    "html": "<div class='div foo'></div><div class='div' _='on click take .foo .bar'></div><div class='div bar'></div>",
    "body": "\n\t\tawait html(\"<div class='div foo'></div><div class='div' _='on click take .foo .bar'></div><div class='div bar'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/bar/);\n\t\tawait expect(find('div').nth(2)).not.toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "can take multiple classes from specific element",
    "html": "<div class='div1 foo bar'></div><div class='div' _='on click take .foo .bar from .div1'></div><div class='div bar'></div>",
    "body": "\n\t\tawait html(\"<div class='div1 foo bar'></div><div class='div' _='on click take .foo .bar from .div1'></div><div class='div bar'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).not.toHaveClass(/foo/);\n\t\tawait expect(find('div').first()).not.toHaveClass(/bar/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/foo/);\n\t\tawait expect(find('div').nth(1)).toHaveClass(/bar/);\n\t\t// third div still has bar since it's .div not .div1\n\t\tawait expect(find('div').nth(2)).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "take",
    "name": "giving may follow the from clause as an alternative to with",
    "html": "<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz from .div giving \"qux\"'></div><div class='div'></div>",
    "body": "\n\t\tawait html(\"<div class='div' data-foo='bar'></div><div class='div' _='on click take @data-foo=baz from .div giving \\\"qux\\\"'></div><div class='div'></div>\");\n\t\tawait find('div').nth(1).dispatchEvent('click');\n\t\tawait expect(find('div').first()).toHaveAttribute('data-foo', 'qux');\n\t\tawait expect(find('div').nth(1)).toHaveAttribute('data-foo', 'baz');\n\t\tawait expect(find('div').nth(2)).toHaveAttribute('data-foo', 'qux');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "attributes refer to the thing being told",
    "html": "<div id='d1' _='on click                           tell #d2                            put @foo into me'></div><div foo='bar' id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            put @foo into me'></div>\" +\n\t\t\t\t\"<div foo='bar' id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "does not overwrite the me symbol",
    "html": "<div id='d1' _='on click                           add .foo                           tell #d2                            add .bar to me'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          add .foo \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            add .bar to me'></div>\" +\n\t\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveClass(/bar/);\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "establishes a proper beingTold symbol",
    "html": "<div id='d1' _='on click                           add .foo                           tell #d2                            add .bar'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          add .foo \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            add .bar'></div>\" +\n\t\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t\tawait expect(find('#d2')).toHaveClass(/bar/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "ignores null",
    "html": "<div id='d1' _='on click                           tell null                            add .bar                          end                          add .foo'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          tell null\" +\n\t\t\t\t\"                            add .bar\" +\n\t\t\t\t\"                          end\" +\n\t\t\t\t\"                          add .foo'></div>\" +\n\t\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "restores a proper implicit me symbol",
    "html": "<div id='d1' _='on click                           tell #d2                            add .bar                          end                          add .foo'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            add .bar\" +\n\t\t\t\t\"                          end\" +\n\t\t\t\t\"                          add .foo'></div>\" +\n\t\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t\tawait expect(find('#d2')).toHaveClass(/bar/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "tell terminates with a feature",
    "html": "<div id=\"d1\" _=\"on click tell #d2 remove yourself on click tell #d3 remove yourself\"><div id=\"d2\"></div><div id=\"d3\"></div></div>",
    "body": "\n\t\tawait html(`<div id=\"d1\" _=\"on click tell #d2 remove yourself on click tell #d3 remove yourself\"><div id=\"d2\"></div><div id=\"d3\"></div></div>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tconst innerHTML = await evaluate(() => document.querySelector('#d1').innerHTML);\n\t\texpect(innerHTML).toBe(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "works with an array",
    "html": "<div id='d1' _='on click                           add .foo                           tell <p/> in me                            add .bar'><p id='p1'></p><p id='p2'></p><div id='d2'></div></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          add .foo \" +\n\t\t\t\t\"                          tell <p/> in me\" +\n\t\t\t\t\"                            add .bar'><p id='p1'></p><p id='p2'></p><div id='d2'></div></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveClass(/foo/);\n\t\tawait expect(find('#d1')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d2')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#p1')).toHaveClass(/bar/);\n\t\tawait expect(find('#p2')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "you symbol represents the thing being told",
    "html": "<div id='d1' _='on click                           tell #d2                            add .bar to you'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            add .bar to you'></div>\" +\n\t\t\t\t\"<div id='d2'></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).not.toHaveClass(/bar/);\n\t\tawait expect(find('#d2')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "your symbol represents the thing being told",
    "html": "<div id='d1' _='on click                           tell #d2                            put your innerText into me'></div><div id='d2'>foo</div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click \" +\n\t\t\t\t\"                          tell #d2\" +\n\t\t\t\t\"                            put your innerText into me'></div>\" +\n\t\t\t\t\"<div id='d2'>foo</div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "tell",
    "name": "yourself attribute also works",
    "html": "<div id=\"d1\" _=\"on click tell #d2 remove yourself\"><div id=\"d2\"></div></div>",
    "body": "\n\t\tawait html(`<div id=\"d1\" _=\"on click tell #d2 remove yourself\"><div id=\"d2\"></div></div>`);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCount(0);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "all html entities escaped",
    "html": "<script type=\"text/hyperscript-template\">${x}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${x}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x then put it into window.res\", {\n                locals: { x: '<div class=\"a\" data-x=\\'b\\'>&', tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('&lt;div class=&quot;a&quot; data-x=&#039;b&#039;&gt;&amp;')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "async expressions in a loop resolve correctly",
    "html": "<script type=\"text/hyperscript-template\">#for x in items\n${asyncFn(x)}\n#end</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for x in items\\n${asyncFn(x)}\\n#end</script>')\n        await evaluate(() => {\n            window.asyncFn = (v) => Promise.resolve(\"got:\" + v)\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            return _hyperscript(\"render tmpl with items: items, asyncFn: asyncFn then put it into window.res\", {\n                locals: { items: [1, 2, 3], asyncFn: window.asyncFn, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('got:1\\ngot:2\\ngot:3\\n')\n    ",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "templates/templates",
    "name": "blank lines are consumed as whitespace",
    "html": "<script type=\"text/hyperscript-template\">a\n\nb\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">a\\n\\nb\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('a\\nb\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "break prevents else clause from executing",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\n#if item === 2\n#break\n#end\n${item}\n#else\nNo items\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\n#if item === 2\\n#break\\n#end\\n${item}\\n#else\\nNo items\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [1, 2, 3], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('1\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "can render",
    "html": "<script type=\"text/hyperscript-template\">render ${x}</script>",
    "body": "\n\t\tawait html('<script type=\"text/hyperscript-template\">render ${x}</script>')\n\t\tawait evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n\t\t\t_hyperscript(\"render tmpl with x: x then put it into window.res\", {\n\t\t\t\tlocals: { x: \":)\", tmpl }\n\t\t\t})\n\t\t})\n\t\tconst res = await evaluate(() => window.res)\n\t\texpect(res).toBe('render :)')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "can render correctly",
    "html": "<script type=\"text/hyperscript-template\">#for x in stuff\n<p>Hello ${x}</p>\n#end</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for x in stuff\\n<p>Hello ${x}</p>\\n#end</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with stuff: stuff then put it into window.res\", {\n                locals: { stuff: [1,2,3], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('<p>Hello 1</p>\\n<p>Hello 2</p>\\n<p>Hello 3</p>\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "conditional with nested parens in value does not false-trigger on inner if",
    "html": "<script type=\"text/hyperscript-template\">${fn(x) if condition}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${fn(x) if condition}</script>')\n        await evaluate(() => {\n            window.testFn = (v) => 'called:' + v\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with fn: fn, x: x, condition: condition then put it into window.res\", {\n                locals: { fn: window.testFn, x: 42, condition: true, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('called:42')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "content after for...else still renders",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\n${item}\n#else\nnothing\n#end\nafter\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\n${item}\\n#else\\nnothing\\n#end\\nafter\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('nothing\\nafter\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "empty template renders empty string",
    "html": "<script type=\"text/hyperscript-template\"></script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\"></script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "error in conditional expression is reported",
    "html": "<script type=\"text/hyperscript-template\">${value if !!!}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${value if !!!}</script>')\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with value: 'hi' then put it into window.res\", { locals: { tmpl } })\n            console.error = origError\n        })\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThan(0)\n        expect(captured[0]).toContain('template error')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "error in top-level expression is reported",
    "html": "<script type=\"text/hyperscript-template\">before ${!!!} after</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">before ${!!!} after</script>')\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", { locals: { tmpl } })\n            console.error = origError\n        })\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThan(0)\n        expect(captured[0]).toContain('template error')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "error inside for body is reported",
    "html": "<script type=\"text/hyperscript-template\">#for x in items\n${!!!}\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for x in items\\n${!!!}\\n#end\\n</script>')\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [1, 2], tmpl }\n            })\n            console.error = origError\n        })\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThan(0)\n        expect(captured[0]).toContain('template error')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "escapes html, with opt-out",
    "html": "<script type=\"text/hyperscript-template\">render ${x} ${unescaped x}</script>",
    "body": "\n\t\tawait html('<script type=\"text/hyperscript-template\">render ${x} ${unescaped x}</script>')\n\t\tawait evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n\t\t\t_hyperscript(\"render tmpl with x: x then put it into window.res\", {\n\t\t\t\tlocals: { x: \"<br>\", tmpl }\n\t\t\t})\n\t\t})\n\t\tconst res = await evaluate(() => window.res)\n\t\texpect(res).toBe('render &lt;br&gt; <br>')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "expression with function call",
    "html": "<script type=\"text/hyperscript-template\">${fn()}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${fn()}</script>')\n        await evaluate(() => {\n            window.testFn = () => \"called\"\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with fn: fn then put it into window.res\", {\n                locals: { fn: window.testFn, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('called')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "expression with math",
    "html": "<script type=\"text/hyperscript-template\">${x + y}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${x + y}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x, y: y then put it into window.res\", {\n                locals: { x: 10, y: 20, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('30')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "expression with nested braces",
    "html": "<script type=\"text/hyperscript-template\">${obj[\"key\"]}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${obj[\"key\"]}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with obj: obj then put it into window.res\", {\n                locals: { obj: { key: \"val\" }, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('val')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "for loop over empty array",
    "html": "<script type=\"text/hyperscript-template\">before\n#for x in items\n${x}\n#end\nafter\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">before\\n#for x in items\\n${x}\\n#end\\nafter\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('before\\nafter\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "for loop with index",
    "html": "<script type=\"text/hyperscript-template\">#for x in items\n${x}\n#end</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for x in items\\n${x}\\n#end</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [\"a\", \"b\", \"c\"], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('a\\nb\\nc\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "good expressions render despite errors in other expressions",
    "html": "<script type=\"text/hyperscript-template\">good: ${value} bad: ${!!!}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">good: ${value} bad: ${!!!}</script>')\n        await evaluate(() => {\n            var origError = console.error\n            console.error = function() {}\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with value: value then put it into window.res\", {\n                locals: { value: 'ok', tmpl }\n            })\n            console.error = origError\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toContain('good: ok')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "good expressions still render alongside bad ones",
    "html": "<script type=\"text/hyperscript-template\">${x} ${!!!} ${y}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${x} ${!!!} ${y}</script>')\n        await evaluate(() => {\n            var origError = console.error\n            console.error = function() {}\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x, y: y then put it into window.res\", {\n                locals: { x: \"hello\", y: \"world\", tmpl }\n            })\n            console.error = origError\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('hello  world')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "handles async expressions",
    "html": "<script type=\"text/hyperscript-template\">result: ${asyncFn()}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">result: ${asyncFn()}</script>')\n        await evaluate(() => {\n            window.asyncFn = () => Promise.resolve(10)\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            return _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('result: 10')\n    ",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "templates/templates",
    "name": "if false takes else branch",
    "html": "<script type=\"text/hyperscript-template\">#if false\nyes\n#else\nno\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#if false\\nyes\\n#else\\nno\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('no\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "if with expression condition",
    "html": "<script type=\"text/hyperscript-template\">#if x is greater than 5\nbig\n#else\nsmall\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#if x is greater than 5\\nbig\\n#else\\nsmall\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x then put it into window.res\", {\n                locals: { x: 10, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('big\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "multiple errors in one template are all reported",
    "html": "<script type=\"text/hyperscript-template\">${!!!}\n${---}\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${!!!}\\n${---}\\n</script>')\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", { locals: { tmpl } })\n            console.error = origError\n        })\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThanOrEqual(2)\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "multiple expressions on one line",
    "html": "<script type=\"text/hyperscript-template\">${a} + ${b} = ${c}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">${a} + ${b} = ${c}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with a: a, b: b, c: c then put it into window.res\", {\n                locals: { a: 1, b: 2, c: 3, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('1 + 2 = 3')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "null and undefined render as empty",
    "html": "<script type=\"text/hyperscript-template\">[${x}][${y}]</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">[${x}][${y}]</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x, y: y then put it into window.res\", {\n                locals: { x: null, y: undefined, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('[][]')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "null values render as empty string, not \"null\"",
    "html": "<script type=\"text/hyperscript-template\">[${x}]</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">[${x}]</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with x: x then put it into window.res\", {\n                locals: { x: null, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('[]')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "plain text with no expressions",
    "html": "<script type=\"text/hyperscript-template\">just plain text\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">just plain text\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('just plain text\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "recovers from bad expression in ${}",
    "html": "<script type=\"text/hyperscript-template\">before ${!!!} after</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">before ${!!!} after</script>')\n        var errors = []\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n            console.error = origError\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('before  after')\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThan(0)\n        expect(captured[0]).toContain('template error')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "recovers from unterminated ${}",
    "html": "<script type=\"text/hyperscript-template\">before ${x after</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">before ${x after</script>')\n        await evaluate(() => {\n            var origError = console.error\n            window.__capturedErrors = []\n            console.error = function() { window.__capturedErrors.push(Array.from(arguments).join(' ')) }\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl then put it into window.res\", {\n                locals: { tmpl }\n            })\n            console.error = origError\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('before ')\n        const captured = await evaluate(() => window.__capturedErrors)\n        expect(captured.length).toBeGreaterThan(0)\n        expect(captured[0]).toContain('Unterminated')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "render here sets innerHTML of me",
    "html": "<div id=\"target\" _=\"on click render #tmpl here\"><script type=\"text/hyperscript-template\" id=\"tmpl\"><b>hello</b></script></div>",
    "body": "\n        await html('<div id=\"target\" _=\"on click render #tmpl here\"><script type=\"text/hyperscript-template\" id=\"tmpl\"><b>hello</b></script></div>')\n        await evaluate(() => document.querySelector('#target').click())\n        await expect(find('#target b')).toHaveText('hello')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "render here with args",
    "html": "<div id=\"target\" _=\"on click render #tmpl with x: 'world' here\"><script type=\"text/hyperscript-template\" id=\"tmpl\"><b>${x}</b></script></div>",
    "body": "\n        await html('<div id=\"target\" _=\"on click render #tmpl with x: \\'world\\' here\"><script type=\"text/hyperscript-template\" id=\"tmpl\"><b>${x}</b></script></div>')\n        await evaluate(() => document.querySelector('#target').click())\n        await expect(find('#target b')).toHaveText('world')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "render into sets innerHTML of target element",
    "html": "<script type=\"text/hyperscript-template\" id=\"tmpl\"><b>hello</b></script><div id=\"target\"></div><button _=\"on click render #tmpl into #target\">go</button>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\" id=\"tmpl\"><b>hello</b></script><div id=\"target\"></div><button _=\"on click render #tmpl into #target\">go</button>')\n        await evaluate(() => document.querySelector('button').click())\n        await expect(find('#target b')).toHaveText('hello')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "render into with args",
    "html": "<script type=\"text/hyperscript-template\" id=\"tmpl\"><b>${x}</b></script><div id=\"target\"></div><button _=\"on click render #tmpl with x: 'world' into #target\">go</button>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\" id=\"tmpl\"><b>${x}</b></script><div id=\"target\"></div><button _=\"on click render #tmpl with x: \\'world\\' into #target\">go</button>')\n        await evaluate(() => document.querySelector('button').click())\n        await expect(find('#target b')).toHaveText('world')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "renders into DOM element",
    "html": "<script type=\"text/hyperscript-template\"><b>${x}</b></script><div id=\"target\"></div>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\"><b>${x}</b></script><div id=\"target\"></div>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            const target = document.querySelector('#target')\n            _hyperscript(\"render tmpl with x: x then put it into window.res\", {\n                locals: { x: \"hello\", tmpl }\n            })\n            target.innerHTML = window.res\n        })\n        await expect(find('#target b')).toHaveText('hello')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports break in template for loops",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\n#if item === 3\n#break\n#end\n${item}\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\n#if item === 3\\n#break\\n#end\\n${item}\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [1, 2, 3, 4, 5], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('1\\n2\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports conditional expressions in loops",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\n${item.name if item.show else \"Hidden\"}\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\n${item.name if item.show else \"Hidden\"}\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: {\n                    items: [\n                        { name: 'Apple', show: true },\n                        { name: 'Banana', show: false },\n                        { name: 'Cherry', show: true }\n                    ],\n                    tmpl\n                }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Apple\\nHidden\\nCherry\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports conditional expressions with complex expressions",
    "html": "<script type=\"text/hyperscript-template\">Status: ${user.name if user.active else \"Inactive\"}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">Status: ${user.name if user.active else \"Inactive\"}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with user: user then put it into window.res\", {\n                locals: { user: { name: 'Alice', active: true }, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Status: Alice')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports conditional expressions with if",
    "html": "<script type=\"text/hyperscript-template\">Result: ${value if condition}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">Result: ${value if condition}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with value: value, condition: condition then put it into window.res\", {\n                locals: { value: 'Hello', condition: true, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Result: Hello')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports conditional expressions with if (false condition)",
    "html": "<script type=\"text/hyperscript-template\">Result: ${value if condition}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">Result: ${value if condition}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with value: value, condition: condition then put it into window.res\", {\n                locals: { value: 'Hello', condition: false, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Result: ')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports conditional expressions with if...else",
    "html": "<script type=\"text/hyperscript-template\">Result: ${value if condition else fallback}</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">Result: ${value if condition else fallback}</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with value: value, condition: condition, fallback: fallback then put it into window.res\", {\n                locals: { value: 'Hello', condition: false, fallback: 'Goodbye', tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Result: Goodbye')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports continue in template for loops",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\n#if item === 2\n#continue\n#end\n${item}\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\n#if item === 2\\n#continue\\n#end\\n${item}\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [1, 2, 3, 4], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('1\\n3\\n4\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports for...else with empty collection",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\nFound: ${item}\n#else\nNo items found\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\nFound: ${item}\\n#else\\nNo items found\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('No items found\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports for...else with non-empty collection",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\nFound: ${item}\n#else\nNo items found\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\nFound: ${item}\\n#else\\nNo items found\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: [1, 2, 3], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Found: 1\\nFound: 2\\nFound: 3\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports for...else with null collection",
    "html": "<script type=\"text/hyperscript-template\">#for item in items\nFound: ${item}\n#else\nNothing to show\n#end\n</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for item in items\\nFound: ${item}\\n#else\\nNothing to show\\n#end\\n</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with items: items then put it into window.res\", {\n                locals: { items: null, tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('Nothing to show\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports if",
    "html": "<script type=\"text/hyperscript-template\">begin\n#if true\na\n#else\nb\n#end\nend\n</script>",
    "body": "\n\t\tawait html('<script type=\"text/hyperscript-template\">begin\\n#if true\\na\\n#else\\nb\\n#end\\nend\\n</script>')\n\t\tawait evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n\t\t\t_hyperscript(\"render tmpl then put it into window.res\", {\n\t\t\t\tlocals: { tmpl }\n\t\t\t})\n\t\t})\n\t\tconst res = await evaluate(() => window.res)\n\t\texpect(res).toBe('begin\\na\\nend\\n')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports nested operations",
    "html": "<script type=\"text/hyperscript-template\">#for x in stuff\n#if x === 2\n<p>Should be 2 -> ${x}</p>\n#end\n#end</script>",
    "body": "\n        await html('<script type=\"text/hyperscript-template\">#for x in stuff\\n#if x === 2\\n<p>Should be 2 -> ${x}</p>\\n#end\\n#end</script>')\n        await evaluate(() => {\n            const tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n            _hyperscript(\"render tmpl with stuff: stuff then put it into window.res\", {\n                locals: { stuff: [0,1,2,2,3,4], tmpl }\n            })\n        })\n        const res = await evaluate(() => window.res)\n        expect(res).toBe('<p>Should be 2 -> 2</p>\\n<p>Should be 2 -> 2</p>\\n')\n    ",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "templates/templates",
    "name": "supports repeat",
    "html": "<script type=\"text/hyperscript-template\">begin\n#for it in [1, 2, 3]\n${it}\n#end\nend\n</script>",
    "body": "\n\t\tawait html('<script type=\"text/hyperscript-template\">begin\\n#for it in [1, 2, 3]\\n${it}\\n#end\\nend\\n</script>')\n\t\tawait evaluate(() => {\n\t\t\tconst tmpl = document.querySelector('#work-area script[type=\"text/hyperscript-template\"]')\n\t\t\t_hyperscript(\"render tmpl then put it into window.res\", {\n\t\t\t\tlocals: { tmpl }\n\t\t\t})\n\t\t})\n\t\tconst res = await evaluate(() => window.res)\n\t\texpect(res).toBe('begin\\n1\\n2\\n3\\nend\\n')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "throw",
    "name": "async exceptions as throws propagate properly",
    "html": "<script type='text/hyperscript'>def foo() wait 2ms call bar() end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo() wait 2ms call bar() end</script>\"\n\t\t);\n\t\tawait evaluate(() => {\n\t\t\twindow.bar = function () { throw \"foo\"; };\n\t\t});\n\t\tconst result = await evaluate(() => foo().catch(e => e));\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "throw",
    "name": "async exceptions propagate properly",
    "html": "<script type='text/hyperscript'>def foo() wait 2ms throw \"foo\" end def bar() call foo() end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>\" +\n\t\t\t\t\"def foo() wait 2ms throw \\\"foo\\\" end \" +\n\t\t\t\t\"def bar() call foo() end\" +\n\t\t\t\t\"</script>\"\n\t\t);\n\t\tconst result = await evaluate(() => bar().catch(e => e));\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "throw",
    "name": "can respond to async exceptions in an event handler with an event handler",
    "html": "<div id='d1' _='on click wait 2ms then throw \"foo\" then put \"bar\" into my.innerHTML end                                 on exception(error) put error into my.innerHTML end '></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div id=\\'d1\\' _=\\'on click wait 2ms then throw \"foo\" then put \"bar\" into my.innerHTML end' +\n\t\t\t\t\"                                 on exception(error) put error into my.innerHTML end '></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "throw",
    "name": "can respond to exceptions in an event handler with an event handler",
    "html": "<div id='d1' _='on click throw \"foo\" then put \"bar\" into my.innerHTML end                                 on exception(error) put error into my.innerHTML end '></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div id=\\'d1\\' _=\\'on click throw \"foo\" then put \"bar\" into my.innerHTML end' +\n\t\t\t\t\"                                 on exception(error) put error into my.innerHTML end '></div>\"\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"foo\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "throw",
    "name": "can throw a basic exception",
    "html": "<script type='text/hyperscript'>def foo() throw \"foo\" end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo() throw \\\"foo\\\" end</script>\"\n\t\t);\n\t\tconst result = await evaluate(() => {\n\t\t\ttry { foo(); return \"no throw\"; } catch (e) { return e; }\n\t\t});\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "throw",
    "name": "can throw an async exception",
    "html": "<script type='text/hyperscript'>def foo() wait 2ms throw \"foo\" end</script>",
    "body": "\n\t\tawait html(\n\t\t\t\"<script type='text/hyperscript'>def foo() wait 2ms throw \\\"foo\\\" end</script>\"\n\t\t);\n\t\tconst result = await evaluate(() => foo().catch(e => e));\n\t\texpect(result).toBe(\"foo\");\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "throw",
    "name": "can throw inside an event handler",
    "html": "<div id='d1' _='on click throw \"foo\" then                                          put \"bar\" into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div id='d1' _='on click throw \\\"foo\\\" then\" +\n\t\t\t\t'                                          put \"bar\" into my.innerHTML\\'></div>'\n\t\t);\n\t\tawait find('#d1').dispatchEvent('click');\n\t\tawait expect(find('#d1')).toHaveText(\"\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can target another div for class ref toggle",
    "html": "<div id='bar'></div><div _='on click toggle .foo on #bar'></div>",
    "body": "\n\t\tawait html(\"<div id='bar'></div><div _='on click toggle .foo on #bar'></div>\");\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo/);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#bar')).toHaveClass(/foo/);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('#bar')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle *display between two values",
    "html": "<div style='display:none' _=\"on click toggle *display of me between 'none' and 'flex'\"></div>",
    "body": "\n\t\tawait html(\"<div style='display:none' _=\\\"on click toggle *display of me between 'none' and 'flex'\\\"></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'flex');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle *opacity between three values",
    "html": "<div style='opacity:0' _=\"on click toggle *opacity of me between '0', '0.5' and '1'\"></div>",
    "body": "\n\t\tawait html(\"<div style='opacity:0' _=\\\"on click toggle *opacity of me between '0', '0.5' and '1'\\\"></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0.5');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle a global variable between three values",
    "html": "<div _=\"on click toggle $state between 'a', 'b' and 'c'\"></div>",
    "body": "\n\t\tawait html(\"<div _=\\\"on click toggle $state between 'a', 'b' and 'c'\\\"></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tvar val = await evaluate(() => window.$state);\n\t\texpect(val).toBe('a');\n\t\tawait find('div').dispatchEvent('click');\n\t\tval = await evaluate(() => window.$state);\n\t\texpect(val).toBe('b');\n\t\tawait find('div').dispatchEvent('click');\n\t\tval = await evaluate(() => window.$state);\n\t\texpect(val).toBe('c');\n\t\tawait find('div').dispatchEvent('click');\n\t\tval = await evaluate(() => window.$state);\n\t\texpect(val).toBe('a');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle a global variable between two values",
    "html": "<div _=\"on click toggle $mode between 'edit' and 'preview'\"></div>",
    "body": "\n\t\tawait html(\"<div _=\\\"on click toggle $mode between 'edit' and 'preview'\\\"></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tvar val = await evaluate(() => window.$mode);\n\t\texpect(val).toBe('edit');\n\t\tawait find('div').dispatchEvent('click');\n\t\tval = await evaluate(() => window.$mode);\n\t\texpect(val).toBe('preview');\n\t\tawait find('div').dispatchEvent('click');\n\t\tval = await evaluate(() => window.$mode);\n\t\texpect(val).toBe('edit');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle between different attributes",
    "html": "<div enabled='true' _=\"on click toggle between [@enabled='true'] and [@disabled='true']\"></div>",
    "body": "\n\t\tawait html(\"<div enabled='true' _=\\\"on click toggle between [@enabled='true'] and [@disabled='true']\\\"></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('enabled', 'true');\n\t\tawait find('div').dispatchEvent('click');\n\t\texpect(await find('div').getAttribute('enabled')).toBeNull();\n\t\tawait expect(find('div')).toHaveAttribute('disabled', 'true');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('enabled', 'true');\n\t\texpect(await find('div').getAttribute('disabled')).toBeNull();\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle between two attribute values",
    "html": "<div data-state='active' _=\"on click toggle between [@data-state='active'] and [@data-state='inactive']\"></div>",
    "body": "\n\t\tawait html(\"<div data-state='active' _=\\\"on click toggle between [@data-state='active'] and [@data-state='inactive']\\\"></div>\");\n\t\tawait expect(find('div')).toHaveAttribute('data-state', 'active');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('data-state', 'inactive');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('data-state', 'active');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle between two classes",
    "html": "<div class='foo' _='on click toggle between .foo and .bar'></div>",
    "body": "\n\t\tawait html(\"<div class='foo' _='on click toggle between .foo and .bar'></div>\");\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle class ref on a single div",
    "html": "<div _='on click toggle .foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle .foo'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle class ref on a single form",
    "html": "<form _='on click toggle .foo'></form>",
    "body": "\n\t\tawait html(\"<form _='on click toggle .foo'></form>\");\n\t\tawait expect(find('form')).not.toHaveClass(/foo/);\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('form')).toHaveClass(/foo/);\n\t\tawait find('form').dispatchEvent('click');\n\t\tawait expect(find('form')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle crazy tailwinds class ref on a single form",
    "html": "<form _='on click toggle .group-\\[:nth-of-type\\(3\\)_\\&\\]:block'></form>",
    "body": "\n\t\tawait html(\"<form _='on click toggle .group-\\\\[:nth-of-type\\\\(3\\\\)_\\\\&\\\\]:block'></form>\");\n\t\tawait find('form').dispatchEvent('click');\n\t\tconst hasClass = await evaluate(() =>\n\t\t\tdocument.querySelector('#work-area form').classList.contains(\"group-[:nth-of-type(3)_&]:block\")\n\t\t);\n\t\texpect(hasClass).toBe(true);\n\t\tawait find('form').dispatchEvent('click');\n\t\tconst hasClass2 = await evaluate(() =>\n\t\t\tdocument.querySelector('#work-area form').classList.contains(\"group-[:nth-of-type(3)_&]:block\")\n\t\t);\n\t\texpect(hasClass2).toBe(false);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle display",
    "html": "<div _='on click toggle *display'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle *display'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle display on other elt",
    "html": "<div _='on click toggle the *display of #d2'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle the *display of #d2'></div><div id='d2'></div>\");\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'block');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'none');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle display w/ my",
    "html": "<div _='on click toggle my *display'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle my *display'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'none');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('display', 'block');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle for a fixed amount of time",
    "html": "<div _='on click toggle .foo for 10ms'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle .foo for 10ms'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle multiple class refs",
    "html": "<div class='bar' _='on click toggle .foo .bar'></div>",
    "body": "\n\t\tawait html(\"<div class='bar' _='on click toggle .foo .bar'></div>\");\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle non-class attributes",
    "html": "<div _='on click toggle [@foo=\"bar\"]'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle [@foo=\\\"bar\\\"]'></div>\");\n\t\tawait expect(find('div')).not.toHaveAttribute('foo');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveAttribute('foo', 'bar');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveAttribute('foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle non-class attributes on selects",
    "html": "<select _='on click toggle [@foo=\"bar\"]'></select>",
    "body": "\n\t\tawait html(\"<select _='on click toggle [@foo=\\\"bar\\\"]'></select>\");\n\t\tawait expect(find('select')).not.toHaveAttribute('foo');\n\t\tawait find('select').dispatchEvent('click');\n\t\tawait expect(find('select')).toHaveAttribute('foo', 'bar');\n\t\tawait find('select').dispatchEvent('click');\n\t\tawait expect(find('select')).not.toHaveAttribute('foo');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle opacity",
    "html": "<div _='on click toggle *opacity'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle *opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle opacity on other elt",
    "html": "<div _='on click toggle the *opacity of #d2'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle the *opacity of #d2'></div><div id='d2'></div>\");\n\t\tawait expect(find('#d2')).toHaveCSS('opacity', '1');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('opacity', '0');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle opacity w/ my",
    "html": "<div _='on click toggle my *opacity'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle my *opacity'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '0');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('opacity', '1');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle until an event on another element",
    "html": "<div id='d1'></div><div _='on click toggle .foo until foo from #d1'></div>",
    "body": "\n\t\tawait html(\"<div id='d1'></div><div _='on click toggle .foo until foo from #d1'></div>\");\n\t\tawait expect(find('div:nth-of-type(2)')).not.toHaveClass(/foo/);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('div:nth-of-type(2)')).toHaveClass(/foo/);\n\t\tawait evaluate(() => document.querySelector('#d1').dispatchEvent(new CustomEvent(\"foo\")));\n\t\tawait expect(find('div:nth-of-type(2)')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle visibility",
    "html": "<div _='on click toggle *visibility'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle *visibility'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'hidden');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle visibility on other elt",
    "html": "<div _='on click toggle the *visibility of #d2'></div><div id='d2'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle the *visibility of #d2'></div><div id='d2'></div>\");\n\t\tawait expect(find('#d2')).toHaveCSS('visibility', 'visible');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('visibility', 'hidden');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#d2')).toHaveCSS('visibility', 'visible');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "toggle",
    "name": "can toggle visibility w/ my",
    "html": "<div _='on click toggle my *visibility'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle my *visibility'></div>\");\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'hidden');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('visibility', 'visible');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on current element",
    "html": "<div _='on click transition *width from 0px to 100px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click transition *width from 0px to 100px'></div>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area div', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on current element using style ref",
    "html": "<div _='on click transition *width from 0px to 100px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click transition *width from 0px to 100px'></div>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area div', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on current element with the my prefix",
    "html": "<div _='on click transition my *width from 0px to 100px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click transition my *width from 0px to 100px'></div>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area div', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on current element with the my prefix using style ref",
    "html": "<div _='on click transition my *width from 0px to 100px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click transition my *width from 0px to 100px'></div>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area div', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on form",
    "html": "<form _='on click transition *width from 0px to 100px'></form>",
    "body": "\n\t\tawait html(\"<form _='on click transition *width from 0px to 100px'></form>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area form', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('form')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition a single property on form using style ref",
    "html": "<form _='on click transition *width from 0px to 100px'></form>",
    "body": "\n\t\tawait html(\"<form _='on click transition *width from 0px to 100px'></form>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area form', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('form')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on another element",
    "html": "<div _=\"on click transition #foo's *width from 0px to 100px\"></div><div id=\"foo\"></div>",
    "body": "\n\t\tawait html('<div _=\"on click transition #foo\\'s *width from 0px to 100px\"></div><div id=\"foo\"></div>');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on another element with it",
    "html": "<div _='on click get #foo then transition its *width from 0px to 100px'></div><div id='foo'></div>",
    "body": "\n\t\tawait html(\"<div _='on click get #foo then transition its *width from 0px to 100px'></div><div id='foo'></div>\");\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on another element with of syntax",
    "html": "<div _=\"on click transition *width of #foo from 0px to 100px\"></div><div id=\"foo\"></div>",
    "body": "\n\t\tawait html('<div _=\"on click transition *width of #foo from 0px to 100px\"></div><div id=\"foo\"></div>');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on another element with possessive",
    "html": "<div _=\"on click transition #foo's *width from 0px to 100px\"></div><div id=\"foo\"></div>",
    "body": "\n\t\tawait html('<div _=\"on click transition #foo\\'s *width from 0px to 100px\"></div><div id=\"foo\"></div>');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on query ref with of syntax",
    "html": "<div _=\"on click transition *width of the next &lt;span/> from 0px to 100px\"></div><span></span>",
    "body": "\n\t\tawait html(\"<div _=\\\"on click transition *width of the next &lt;span/> from 0px to 100px\\\"></div><span></span>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('span')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition on query ref with possessive",
    "html": "",
    "body": "\n\t\tawait evaluate(() => {\n\t\t\tvar wa = document.getElementById('work-area');\n\t\t\twa.innerHTML = '<div></div><div></div>';\n\t\t\twa.querySelector('div').setAttribute('_', \"on click transition the next <div/>'s *width from 0px to 100px\");\n\t\t\t_hyperscript.process(wa);\n\t\t});\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('div').nth(1)).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "transition",
    "name": "can transition two properties on current element",
    "html": "<div _='on click transition *width from 0px to 100px *height from 0px to 100px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click transition *width from 0px to 100px *height from 0px to 100px'></div>\");\n\t\tconst fromValues = await evaluate(() => {\n\t\t\tconst el = document.querySelector('#work-area div')\n\t\t\tel.dispatchEvent(new Event('click', {bubbles: true}))\n\t\t\treturn {width: el.style.width, height: el.style.height}\n\t\t})\n\t\texpect(fromValues.width).toBe('0px');\n\t\texpect(fromValues.height).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t\tawait expect(find('div')).toHaveCSS('height', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition with a custom transition string",
    "html": "<div _=\"on click transition #foo's *width from 0px to 100px using &quot;width 2s ease-in&quot;\"></div><div id=\"foo\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t'<div _=\"on click transition #foo\\'s *width from 0px to 100px using &quot;width 2s ease-in&quot;\"></div><div id=\"foo\"></div>'\n\t\t);\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition with a custom transition time via the over syntax",
    "html": "<div _=\"on click transition #foo's *width from 0px to 100px over 2s\"></div><div id=\"foo\"></div>",
    "body": "\n\t\tawait html('<div _=\"on click transition #foo\\'s *width from 0px to 100px over 2s\"></div><div id=\"foo\"></div>');\n\t\tawait find('div').first().dispatchEvent('click');\n\t\tawait expect(find('#foo')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can transition with parameterized values",
    "html": "<div _='on click                                set startWidth to 0                               set endWidth to 100                               transition *width from (startWidth)px to (endWidth)px'></div>",
    "body": "\n\t\tawait html(\"<div _='on click \" +\n\t\t\t\"                               set startWidth to 0\" +\n\t\t\t\"                               set endWidth to 100\" +\n\t\t\t\"                               transition *width from (startWidth)px to (endWidth)px'></div>\");\n\t\tconst fromValue = await clickAndReadStyle(evaluate, '#work-area div', 'width')\n\t\texpect(fromValue).toBe('0px');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "transition",
    "name": "can use initial to transition to original value",
    "html": "<div style='width: 10px' _='on click 1 transition my *width to 100px                                               on click 2 transition my *width to initial'></div>",
    "body": "\n\t\tawait html(\"<div style='width: 10px' _='on click 1 transition my *width to 100px \" +\n\t\t\t\"                                              on click 2 transition my *width to initial'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('width', '100px');\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveCSS('width', '10px');\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events",
    "html": "<div _='on click trigger foo end                          on foo add .foo-set end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo end\" + \"                          on foo add .foo-set end'></div>\"\n\t\t);\n\t\tawait expect(find('div')).not.toHaveClass(/foo-set/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo-set/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events with args",
    "html": "<div _='on click trigger foo(x:42) end                          on foo(x) put x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo(x:42) end\" +\n\t\t\t\t\"                          on foo(x) put x into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events with colons",
    "html": "<div _='on click trigger foo:bar end                          on foo:bar add .foo-set end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo:bar end\" + \"                          on foo:bar add .foo-set end'></div>\"\n\t\t);\n\t\tawait expect(find('div')).not.toHaveClass(/foo-set/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo-set/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events with dots",
    "html": "<div _='on click trigger foo.bar end                          on foo.bar add .foo-set end'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo.bar end\" + \"                          on foo.bar add .foo-set end'></div>\"\n\t\t);\n\t\tawait expect(find('div')).not.toHaveClass(/foo-set/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo-set/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events with dots with args",
    "html": "<div _='on click trigger foo.bar(x:42) end                          on foo.bar(x) put x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo.bar(x:42) end\" +\n\t\t\t\t\"                          on foo.bar(x) put x into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "trigger",
    "name": "can trigger events with dots with colons",
    "html": "<div _='on click trigger foo:bar(x:42) end                          on foo:bar(x) put x into my.innerHTML'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click trigger foo:bar(x:42) end\" +\n\t\t\t\t\"                          on foo:bar(x) put x into my.innerHTML'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveText(\"42\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "unlessModifier",
    "name": "unless modifier can conditionally execute a command",
    "html": "<div _='on click toggle .foo unless I match .bar'></div>",
    "body": "\n\t\tawait html(\"<div _='on click toggle .foo unless I match .bar'></div>\");\n\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').classList.add(\"bar\"));\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).not.toHaveClass(/foo/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "accepts an optional 'using' type string",
    "html": "\n\t\t\t<button _='on click\n\t\t\t             start view transition using \"slide\"\n\t\t\t               add .typed to me\n\t\t\t             end'>go</button>\n\t\t",
    "body": "\n\t\tvar capturedTypes = null\n\t\tawait evaluate(() => {\n\t\t\twindow.__vtTypes = null\n\t\t\tdocument.startViewTransition = function (arg) {\n\t\t\t\tif (typeof arg === 'object' && arg.types) window.__vtTypes = arg.types.slice()\n\t\t\t\tvar updateFn = typeof arg === 'function' ? arg : arg.update\n\t\t\t\tupdateFn()\n\t\t\t\treturn { finished: Promise.resolve(), updateCallbackDone: Promise.resolve(), ready: Promise.resolve(), skipTransition: function(){} }\n\t\t\t}\n\t\t})\n\t\tawait html(`\n\t\t\t<button _='on click\n\t\t\t             start view transition using \"slide\"\n\t\t\t               add .typed to me\n\t\t\t             end'>go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/typed/)\n\t\tcapturedTypes = await evaluate(() => window.__vtTypes)\n\t\texpect(capturedTypes).toEqual(['slide'])\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "viewTransition",
    "name": "break inside a loop inside a view transition is NOT replaced",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               repeat 3 times\n\t\t\t                 add .in-loop to me\n\t\t\t                 break\n\t\t\t               end\n\t\t\t               add .after-loop to me\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\tawait installVTMock(evaluate)\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               repeat 3 times\n\t\t\t                 add .in-loop to me\n\t\t\t                 break\n\t\t\t               end\n\t\t\t               add .after-loop to me\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\t// break exited the loop, but the transition body continued to add .after-loop\n\t\tawait expect(find('button')).toHaveClass(/in-loop/)\n\t\tawait expect(find('button')).toHaveClass(/after-loop/)\n\t\tvar log = await evaluate(() => window.__vtLog)\n\t\texpect(log).toContain('start')\n\t\texpect(log).toContain('finished')\n\t\t// skipTransition must NOT have been called: break stayed within the loop\n\t\texpect(log).not.toContain('skipped')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "exit inside a view transition skips the animation",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               exit\n\t\t\t               add .after to me\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\tawait installVTMock(evaluate)\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               exit\n\t\t\t               add .after to me\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/before/)\n\t\tawait expect(find('button')).not.toHaveClass(/after/)\n\t\t// AbortViewTransition calls skipTransition() before exit\n\t\tvar log = await evaluate(() => window.__vtLog)\n\t\texpect(log).toContain('start')\n\t\texpect(log).toContain('skipped')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "halt the event inside a view transition skips the animation",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               halt\n\t\t\t             end\n\t\t\t             add .after to me\">go</button>\n\t\t",
    "body": "\n\t\tawait installVTMock(evaluate)\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               halt\n\t\t\t             end\n\t\t\t             add .after to me\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/before/)\n\t\t// halt stopped the handler before reaching the trailing add\n\t\tawait expect(find('button')).not.toHaveClass(/after/)\n\t\tvar log = await evaluate(() => window.__vtLog)\n\t\texpect(log).toContain('skipped')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "return inside a def called from a view transition skips the animation",
    "html": "\n\t\t\t<script type=\"text/hyperscript\">\n\t\t\t  def escapeIt\n\t\t\t    return 42\n\t\t\t  end\n\t\t\t</script>\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               return\n\t\t\t               add .after to me\n\t\t\t             end\n\t\t\t             add .after-handler to me\">go</button>\n\t\t",
    "body": "\n\t\tawait installVTMock(evaluate)\n\t\tawait html(`\n\t\t\t<script type=\"text/hyperscript\">\n\t\t\t  def escapeIt\n\t\t\t    return 42\n\t\t\t  end\n\t\t\t</script>\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               return\n\t\t\t               add .after to me\n\t\t\t             end\n\t\t\t             add .after-handler to me\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/before/)\n\t\tawait expect(find('button')).not.toHaveClass(/after/)\n\t\t// `return` from on-handler short-circuits everything after the transition too\n\t\tawait expect(find('button')).not.toHaveClass(/after-handler/)\n\t\tvar log = await evaluate(() => window.__vtLog)\n\t\texpect(log).toContain('skipped')\n\t",
    "async": true,
    "complexity": "script-tag"
  },
  {
    "category": "viewTransition",
    "name": "return inside an if branch inside a view transition skips the animation",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               if true\n\t\t\t                 return\n\t\t\t               end\n\t\t\t               add .after to me\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\tawait installVTMock(evaluate)\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .before to me\n\t\t\t               if true\n\t\t\t                 return\n\t\t\t               end\n\t\t\t               add .after to me\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/before/)\n\t\tawait expect(find('button')).not.toHaveClass(/after/)\n\t\tvar log = await evaluate(() => window.__vtLog)\n\t\texpect(log).toContain('skipped')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "runs the body when view transitions API is available",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .done to me\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\t// Stub document.startViewTransition with a minimal mock so the command\n\t\t// takes the \"transition supported\" branch and still executes the body.\n\t\tawait evaluate(() => {\n\t\t\tdocument.startViewTransition = function (arg) {\n\t\t\t\tvar updateFn = typeof arg === 'function' ? arg : arg.update\n\t\t\t\t// Fire the update callback synchronously and report finished.\n\t\t\t\tvar p = updateFn()\n\t\t\t\treturn {\n\t\t\t\t\tfinished: Promise.resolve(),\n\t\t\t\t\tupdateCallbackDone: p,\n\t\t\t\t\tready: Promise.resolve(),\n\t\t\t\t\tskipTransition: function () {},\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .done to me\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/done/)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "viewTransition",
    "name": "runs the body when view transitions API is unavailable",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .done to me\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\t// Remove startViewTransition so the command falls back to direct body execution.\n\t\tawait evaluate(() => { delete document.startViewTransition })\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               add .done to me\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\tawait expect(find('button')).toHaveClass(/done/)\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "viewTransition",
    "name": "throws if a view transition is already in progress",
    "html": "\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               start view transition\n\t\t\t                 add .inner to me\n\t\t\t               end\n\t\t\t             end\">go</button>\n\t\t",
    "body": "\n\t\t// Stub startViewTransition that \"hangs\" (never resolves its update)\n\t\t// so the nested start view transition hits the in-progress guard.\n\t\tawait evaluate(() => {\n\t\t\twindow.__errors = []\n\t\t\twindow.addEventListener('error', (e) => { window.__errors.push(e.message) })\n\t\t\tdocument.startViewTransition = function (arg) {\n\t\t\t\tvar updateFn = typeof arg === 'function' ? arg : arg.update\n\t\t\t\ttry { updateFn() } catch (e) { /* ignore */ }\n\t\t\t\treturn { finished: Promise.resolve(), updateCallbackDone: Promise.resolve(), ready: Promise.resolve(), skipTransition: function(){} }\n\t\t\t}\n\t\t})\n\t\t// The nested form exercises the \"already in progress\" branch inside the\n\t\t// same context - second start view transition on the same `context.meta`.\n\t\tawait html(`\n\t\t\t<button _=\"on click\n\t\t\t             start view transition\n\t\t\t               start view transition\n\t\t\t                 add .inner to me\n\t\t\t               end\n\t\t\t             end\">go</button>\n\t\t`)\n\t\tawait find('button').dispatchEvent('click')\n\t\t// Either the inner throws and is caught at the handler boundary, or\n\t\t// it runs - we don't assert on the exact error text, just that the\n\t\t// click didn't crash the page. The branch is hit either way.\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "wait",
    "name": "can destructure properties in a wait",
    "html": "<div _='on click wait for foo(bar)         then put bar into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click wait for foo(bar) \" + \"        then put bar into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\", { detail: { bar: \"bar\" } })));\n\t\tawait expect(find('div')).toHaveText(\"bar\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "can wait on event",
    "html": "<div _='on click                              add .foo then                              wait for foo then                              add .bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait for foo then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).not.toHaveClass(/bar/);\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\")));\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "can wait on event on another element",
    "html": "<div id='d2'></div><div _='on click                              add .foo then                              wait for foo from #d2 then                              add .bar'></div>",
    "body": "\n\t\tawait html(\"<div id='d2'></div>\" +\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait for foo from #d2 then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div:nth-of-type(2)').dispatchEvent('click');\n\t\tawait expect(find('div:nth-of-type(2)')).toHaveClass(/foo/);\n\t\tawait expect(find('div:nth-of-type(2)')).not.toHaveClass(/bar/);\n\t\tawait evaluate(() => document.querySelector('#d2').dispatchEvent(new CustomEvent(\"foo\")));\n\t\tawait expect(find('div:nth-of-type(2)')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "can wait on event or timeout 1",
    "html": "<div _='on click                              add .foo then                              wait for foo or 0ms then                              add .bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait for foo or 0ms then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "can wait on event or timeout 2",
    "html": "<div _='on click                              add .foo then                              wait for foo or 0ms then                              add .bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait for foo or 0ms then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "can wait on time",
    "html": "<div _='on click                              add .foo then                              wait 20ms then                              add .bar'></div>",
    "body": "\n\t\tawait html(\n\t\t\t\"<div _='on click \" +\n\t\t\t\t\"                             add .foo then \" +\n\t\t\t\t\"                             wait 20ms then \" +\n\t\t\t\t\"                             add .bar'></div>\"\n\t\t);\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait expect(find('div')).toHaveClass(/foo/);\n\t\tawait expect(find('div')).toHaveClass(/bar/);\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "wait",
    "name": "waiting on an event sets 'it' to the event",
    "html": "<div _='on click wait for foo         then put its.detail into me'></div>",
    "body": "\n\t\tawait html(\"<div _='on click wait for foo \" + \"        then put its.detail into me'></div>\");\n\t\tawait find('div').dispatchEvent('click');\n\t\tawait evaluate(() => document.querySelector('#work-area div').dispatchEvent(new CustomEvent(\"foo\", { detail: \"hyperscript is hyper cool\" })));\n\t\tawait expect(find('div')).toHaveText(\"hyperscript is hyper cool\");\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "#element.checked is tracked",
    "html": "<input type=\"checkbox\" id=\"cb-input\" /><span _=\"when #cb-input.checked changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"checkbox\" id=\"cb-input\" />` +\n\t\t\t`<span _=\"when #cb-input.checked changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('false')\n\t\tawait find('#cb-input').check()\n\t\tawait expect.poll(() => find('span').textContent()).toBe('true')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "attribute observers are persistent (not recreated on re-run)",
    "html": "",
    "body": "\n\t\tconst observersCreated = await evaluate(async () => {\n\t\t\tconst OrigMO = window.MutationObserver\n\t\t\tlet count = 0\n\t\t\twindow.MutationObserver = function(cb) {\n\t\t\t\tcount++\n\t\t\t\treturn new OrigMO(cb)\n\t\t\t}\n\t\t\twindow.MutationObserver.prototype = OrigMO.prototype\n\t\t\tconst wa = document.getElementById('work-area')\n\t\t\twa.innerHTML = '<div data-val=\"1\" _=\"when @data-val changes put it into me\"></div>'\n\t\t\t_hyperscript.processNode(wa)\n\t\t\tawait new Promise(r => setTimeout(r, 50))\n\t\t\tconst countAfterInit = count\n\t\t\tfor (let i = 2; i <= 6; i++) {\n\t\t\t\twa.querySelector('div').setAttribute('data-val', String(i))\n\t\t\t\tawait new Promise(r => setTimeout(r, 30))\n\t\t\t}\n\t\t\twindow.MutationObserver = OrigMO\n\t\t\treturn count - countAfterInit\n\t\t})\n\t\texpect(observersCreated).toBe(0)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "auto-tracks compound expressions",
    "html": "<div _=\"when ($a + $b) changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $a to 1\")\n\t\tawait run(\"set $b to 2\")\n\t\tawait html(`<div _=\"when ($a + $b) changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('3')\n\n\t\tawait run(\"set $a to 10\")\n\t\tawait expect(find('div')).toHaveText('12')\n\n\t\tawait run(\"set $b to 20\")\n\t\tawait expect(find('div')).toHaveText('30')\n\t\tawait evaluate(() => { delete window.$a; delete window.$b })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "batches multiple synchronous writes into one effect run",
    "html": "<div _=\"when ($batchA + $batchB) changes increment :runCount then put :runCount into me\"></div>",
    "body": "\n\t\tawait run(\"set $batchA to 0\")\n\t\tawait run(\"set $batchB to 0\")\n\t\tawait html(\n\t\t\t`<div _=\"when ($batchA + $batchB) changes increment :runCount then put :runCount into me\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveText('1')\n\n\t\t// Both writes in a single evaluate so they happen in the same microtask\n\t\tawait evaluate(() => {\n\t\t\t_hyperscript(\"set $batchA to 5\")\n\t\t\t_hyperscript(\"set $batchB to 10\")\n\t\t})\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait evaluate(() => { delete window.$batchA; delete window.$batchB })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "boolean short-circuit does not track unread branch",
    "html": "<div _=\"when ($x and $y) changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $x to false\")\n\t\tawait run(\"set $y to 'hello'\")\n\t\tawait html(`<div _=\"when ($x and $y) changes put it into me\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait run(\"set $y to 'world'\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\texpect(await find('div').textContent()).not.toBe('world')\n\t\tawait evaluate(() => { delete window.$x; delete window.$y })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "circular guard resets after cascade settles",
    "html": "<span _=\"when $ping changes set $ping to (it + 1)\"></span><div _=\"when $ping changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $ping to 0\")\n\t\tawait html(\n\t\t\t`<span _=\"when $ping changes set $ping to (it + 1)\"></span>` +\n\t\t\t`<div _=\"when $ping changes put it into me\"></div>`\n\t\t)\n\t\tawait run(\"set $ping to 1\")\n\t\tawait new Promise(r => setTimeout(r, 500))\n\t\tawait run(\"set $ping to 0\")\n\t\tawait run(\"set $ping to 999\")\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(Number(await find('div').textContent())).toBeGreaterThan(0)\n\t\tawait evaluate(() => { delete window.$ping })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "comparison on tracked symbol works",
    "html": "<div _=\"when ($cmpVal > 5) changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $cmpVal to 3\")\n\t\tawait html(`<div _=\"when ($cmpVal > 5) changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('false')\n\t\tawait run(\"set $cmpVal to 10\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('true')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "cross-microtask ping-pong is caught by circular guard",
    "html": "<span _=\"when $ping changes set $pong to (it + 1)\"></span><span _=\"when $pong changes set $ping to (it + 1)\"></span><div _=\"when $ping changes put it into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<span _=\"when $ping changes set $pong to (it + 1)\"></span>` +\n\t\t\t`<span _=\"when $pong changes set $ping to (it + 1)\"></span>` +\n\t\t\t`<div _=\"when $ping changes put it into me\"></div>`\n\t\t)\n\n\t\t// This creates A->B->A->B... across microtask boundaries\n\t\tawait run(\"set $ping to 1\")\n\t\tawait new Promise(r => setTimeout(r, 1000))\n\n\t\t// The browser should not freeze. The guard should have stopped it.\n\t\t// The value should be finite (not still incrementing).\n\t\tconst val = Number(await find('div').textContent())\n\t\texpect(val).toBeLessThan(200)\n\t\tawait evaluate(() => { delete window.$ping; delete window.$pong })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "detects attribute changes",
    "html": "<div data-title=\"original\" _=\"when @data-title changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div data-title=\"original\" _=\"when @data-title changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('original')\n\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute('data-title', 'updated'))\n\t\t// MutationObserver + effect pipeline is async - poll for the update\n\t\tawait expect.poll(() => find('div').textContent(), { timeout: 5000 }).toBe('updated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "detects changes from $global variable",
    "html": "<div _=\"when $global changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"when $global changes put it into me\"></div>`)\n\t\tawait run(\"set $global to 'Changed!'\")\n\t\tawait expect(find('div')).toHaveText('Changed!')\n\t\tawait evaluate(() => { delete window.$global })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "detects changes from :element variable",
    "html": "<div _=\"init set :count to 0 end\n\t\t\t         when :count changes put it into me end\n\t\t\t         on click increment :count\">0</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set :count to 0 end\n\t\t\t         when :count changes put it into me end\n\t\t\t         on click increment :count\">0</div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveText('0')\n\t\tawait find('div').click()\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait find('div').click()\n\t\tawait expect(find('div')).toHaveText('2')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "detects form input value changes via user interaction",
    "html": "<input type=\"text\" id=\"reactive-input\" value=\"start\" /><span _=\"when #reactive-input.value changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"reactive-input\" value=\"start\" />` +\n\t\t\t`<span _=\"when #reactive-input.value changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('start')\n\n\t\tawait evaluate(() => {\n\t\t\tconst input = document.getElementById('reactive-input')\n\t\t\tinput.value = 'typed'\n\t\t\tinput.dispatchEvent(new Event('input', { bubbles: true }))\n\t\t})\n\t\tawait expect(find('span')).toHaveText('typed')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "detects property change via hyperscript set",
    "html": "<input type=\"text\" id=\"prog-input\" value=\"initial\" /><span _=\"when #prog-input.value changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"prog-input\" value=\"initial\" />` +\n\t\t\t`<span _=\"when #prog-input.value changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('initial')\n\n\t\tawait run(\"set #prog-input.value to 'updated'\")\n\t\tawait expect.poll(() => find('span').textContent()).toBe('updated')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "diamond: cascaded derived values produce correct final value",
    "html": "<span id=\"d-b\" _=\"when $a changes set $b to (it * 2)\"></span><span id=\"d-c\" _=\"when $a changes set $c to (it * 3)\"></span><div _=\"live increment :runs then put ($b + $c) + ' (runs:' + :runs + ')' into me\"></div>",
    "body": "\n\t\tawait run(\"set $a to 1\")\n\t\tawait html(\n\t\t\t`<span id=\"d-b\" _=\"when $a changes set $b to (it * 2)\"></span>` +\n\t\t\t`<span id=\"d-c\" _=\"when $a changes set $c to (it * 3)\"></span>` +\n\t\t\t`<div _=\"live increment :runs then put ($b + $c) + ' (runs:' + :runs + ')' into me\"></div>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait run(\"set $a to 10\")\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await find('div').textContent()).toContain('50')\n\t\tawait evaluate(() => { delete window.$a; delete window.$b; delete window.$c })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "disposes effect when element is removed from DOM",
    "html": "<div _=\"when $dispose changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $dispose to 'before'\")\n\t\tawait html(`<div _=\"when $dispose changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('before')\n\n\t\tconst textBefore = await evaluate(() => {\n\t\t\tconst div = document.querySelector('#work-area div')\n\t\t\tdiv.parentNode.removeChild(div)\n\t\t\treturn div.innerHTML\n\t\t})\n\t\texpect(textBefore).toBe('before')\n\n\t\tawait run(\"set $dispose to 'after'\")\n\t\tawait new Promise(r => setTimeout(r, 50))\n\n\t\t// Element was removed - should still show old value\n\t\tconst textAfter = await evaluate(() => {\n\t\t\t// The div is detached, check it still has old content\n\t\t\treturn true\n\t\t})\n\t\tawait evaluate(() => { delete window.$dispose })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "does not cross-trigger on unrelated variable writes",
    "html": "<div _=\"when $trigger changes\n\t\t\t             increment :count\n\t\t\t             put :count into me\n\t\t\t             set $other to 'side-effect'\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"when $trigger changes\n\t\t\t             increment :count\n\t\t\t             put :count into me\n\t\t\t             set $other to 'side-effect'\"></div>`\n\t\t)\n\t\tawait run(\"set $trigger to 'go'\")\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait new Promise(r => setTimeout(r, 50))\n\t\tawait expect(find('div')).toHaveText('1')\n\t\tawait evaluate(() => { delete window.$trigger; delete window.$other })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "does not execute when variable is undefined initially",
    "html": "<div _=\"when $neverSet changes put 'synced' into me\">original</div>",
    "body": "\n\t\tawait html(`<div _=\"when $neverSet changes put 'synced' into me\">original</div>`)\n\t\t// Wait a bit and verify it didn't change\n\t\tawait new Promise(r => setTimeout(r, 50))\n\t\tawait expect(find('div')).toHaveText('original')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "element moved in DOM retains reactivity",
    "html": "<div id=\"container-a\"><span _=\"when $movable changes put it into me\"></span></div><div id=\"container-b\"></div>",
    "body": "\n\t\tawait run(\"set $movable to 'start'\")\n\t\tawait html(\n\t\t\t`<div id=\"container-a\"><span _=\"when $movable changes put it into me\"></span></div>` +\n\t\t\t`<div id=\"container-b\"></div>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('start')\n\n\t\t// Move the span from container-a to container-b\n\t\tawait evaluate(() => {\n\t\t\tvar span = document.querySelector('#work-area span')\n\t\t\tdocument.getElementById('container-b').appendChild(span)\n\t\t})\n\n\t\t// Element is still connected, just in a different parent\n\t\tawait run(\"set $movable to 'moved'\")\n\t\tawait expect.poll(() => find('span').textContent()).toBe('moved')\n\t\tawait evaluate(() => { delete window.$movable })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "error in one effect does not break other effects in the same batch",
    "html": "<span id=\"err-a\" _=\"when $trigger changes put null.boom into me\"></span><span id=\"err-b\" _=\"when $trigger changes put 'ok:' + it into me\"></span>",
    "body": "\n\t\tawait run(\"set $trigger to 0\")\n\t\tawait html(\n\t\t\t`<span id=\"err-a\" _=\"when $trigger changes put null.boom into me\"></span>` +\n\t\t\t`<span id=\"err-b\" _=\"when $trigger changes put 'ok:' + it into me\"></span>`\n\t\t)\n\t\tawait new Promise(r => setTimeout(r, 50))\n\t\tawait run(\"set $trigger to 42\")\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tawait expect(find('#err-b')).toHaveText('ok:42')\n\t\tawait evaluate(() => { delete window.$trigger })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "executes multiple commands",
    "html": "<div _=\"when $multi changes put 'first' into me then add .executed to me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"when $multi changes put 'first' into me then add .executed to me\"></div>`)\n\t\tawait run(\"set $multi to 'go'\")\n\t\tawait expect(find('div')).toHaveText('first')\n\t\tawait expect(find('div')).toHaveClass(/executed/)\n\t\tawait evaluate(() => { delete window.$multi })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "fires when either expression changes using or",
    "html": "<div _=\"when $x or $y changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"when $x or $y changes put it into me\"></div>`)\n\t\tawait run(\"set $x to 'from-x'\")\n\t\tawait expect(find('div')).toHaveText('from-x')\n\n\t\tawait run(\"set $y to 'from-y'\")\n\t\tawait expect(find('div')).toHaveText('from-y')\n\t\tawait evaluate(() => { delete window.$x; delete window.$y })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "function call on tracked value works (Math.round)",
    "html": "<div _=\"when (Math.round($rawNum)) changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $rawNum to 3.7\")\n\t\tawait html(`<div _=\"when (Math.round($rawNum)) changes put it into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('4')\n\t\tawait run(\"set $rawNum to 9.2\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('9')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "handles NaN without infinite re-firing",
    "html": "<input type=\"text\" id=\"nan-input\" value=\"not a number\" /><span _=\"when (#nan-input.value * 1) changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"nan-input\" value=\"not a number\" />` +\n\t\t\t`<span _=\"when (#nan-input.value * 1) changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('NaN')\n\n\t\tawait evaluate(() => {\n\t\t\tdocument.getElementById('nan-input').dispatchEvent(new Event('input', { bubbles: true }))\n\t\t})\n\t\tawait new Promise(r => setTimeout(r, 50))\n\t\tawait expect(find('span')).toHaveText('NaN')\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "handles chained reactivity across elements",
    "html": "<div _=\"when $source changes set $derived to (it * 2)\"></div><div id=\"output\" _=\"when $derived changes put it into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"when $source changes set $derived to (it * 2)\"></div>` +\n\t\t\t`<div id=\"output\" _=\"when $derived changes put it into me\"></div>`\n\t\t)\n\t\tawait run(\"set $source to 5\")\n\t\tawait expect(find('#output')).toHaveText('10')\n\n\t\tawait run(\"set $source to 20\")\n\t\tawait expect(find('#output')).toHaveText('40')\n\t\tawait evaluate(() => { delete window.$source; delete window.$derived })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "handles rapid successive changes correctly",
    "html": "<div _=\"when $rapid changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"when $rapid changes put it into me\"></div>`)\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tawait run(\"set $rapid to \" + i)\n\t\t}\n\t\tawait expect(find('div')).toHaveText('9')\n\t\tawait evaluate(() => { delete window.$rapid })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "inline style change via JS is NOT detected",
    "html": "<div id=\"style-target\" style=\"opacity: 1\" _=\"when (*opacity) changes put it into me\">not fired</div>",
    "body": "\n\t\tawait html(`<div id=\"style-target\" style=\"opacity: 1\" _=\"when (*opacity) changes put it into me\">not fired</div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tconst initialText = await find('div').textContent()\n\t\tawait evaluate(() => document.getElementById('style-target').style.opacity = '0.5')\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await find('div').textContent()).toBe(initialText)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "isolates element-scoped variables between elements",
    "html": "<div id=\"d1\" _=\"init set :value to 'A' end\n\t\t\t               when :value changes put it into me end\n\t\t\t               on click set :value to 'A-clicked'\">A</div><div id=\"d2\" _=\"init set :value to 'B' end\n\t\t\t               when :value changes put it into me end\n\t\t\t               on click set :value to 'B-clicked'\">B</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"init set :value to 'A' end\n\t\t\t               when :value changes put it into me end\n\t\t\t               on click set :value to 'A-clicked'\">A</div>` +\n\t\t\t`<div id=\"d2\" _=\"init set :value to 'B' end\n\t\t\t               when :value changes put it into me end\n\t\t\t               on click set :value to 'B-clicked'\">B</div>`\n\t\t)\n\t\tawait expect(find('#d1')).toHaveText('A')\n\t\tawait expect(find('#d2')).toHaveText('B')\n\n\t\tawait find('#d1').click()\n\t\tawait expect(find('#d1')).toHaveText('A-clicked')\n\t\tawait expect(find('#d2')).toHaveText('B')\n\n\t\tawait find('#d2').click()\n\t\tawait expect(find('#d2')).toHaveText('B-clicked')\n\t\tawait expect(find('#d1')).toHaveText('A-clicked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "local variable in when expression produces a parse error",
    "html": "",
    "body": "\n\t\tconst error = await evaluate(() => {\n\t\t\tvar origError = console.error\n\t\t\tvar captured = null\n\t\t\tconsole.error = function() {\n\t\t\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\t\t\tvar arg = String(arguments[i])\n\t\t\t\t\tif (arg.includes('local variable')) captured = arg\n\t\t\t\t}\n\t\t\t\torigError.apply(console, arguments)\n\t\t\t}\n\t\t\tvar wa = document.getElementById('work-area')\n\t\t\twa.innerHTML = '<div _=\"when myVar changes put it into me\"></div>'\n\t\t\t_hyperscript.processNode(wa)\n\t\t\tconsole.error = origError\n\t\t\treturn captured\n\t\t})\n\t\texpect(error).not.toBeNull()\n\t",
    "async": true,
    "complexity": "eval-only"
  },
  {
    "category": "when",
    "name": "math on tracked symbols works",
    "html": "<div _=\"when ($mA * $mB) changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $mA to 3\")\n\t\tawait run(\"set $mB to 4\")\n\t\tawait html(`<div _=\"when ($mA * $mB) changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('12')\n\t\tawait run(\"set $mA to 10\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('40')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "mutating array element in place is NOT detected",
    "html": "<div _=\"when $arrMut[0] changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $arrMut to [1, 2, 3]\")\n\t\tawait html(`<div _=\"when $arrMut[0] changes put it into me\"></div>`)\n\t\tawait new Promise(r => setTimeout(r, 100))\n\t\tconst initialText = await find('div').textContent()\n\t\tawait evaluate(() => { window.$arrMut[0] = 99 })\n\t\tawait new Promise(r => setTimeout(r, 200))\n\t\texpect(await find('div').textContent()).toBe(initialText)\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "my @attr is tracked",
    "html": "<div data-x=\"one\" _=\"when my @data-x changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div data-x=\"one\" _=\"when my @data-x changes put it into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('one')\n\t\tawait evaluate(() => document.querySelector('#work-area div').setAttribute('data-x', 'two'))\n\t\tawait expect.poll(() => find('div').textContent()).toBe('two')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "only triggers when variable actually changes value",
    "html": "<div _=\"when $dedup changes increment :callCount then put :callCount into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"when $dedup changes increment :callCount then put :callCount into me\"></div>`\n\t\t)\n\t\tawait run(\"set $dedup to 'value1'\")\n\t\tawait expect(find('div')).toHaveText('1')\n\n\t\t// Same value - should NOT re-trigger\n\t\tawait run(\"set $dedup to 'value1'\")\n\t\tawait new Promise(r => setTimeout(r, 50))\n\t\tawait expect(find('div')).toHaveText('1')\n\n\t\tawait run(\"set $dedup to 'value2'\")\n\t\tawait expect(find('div')).toHaveText('2')\n\t\tawait evaluate(() => { delete window.$dedup })\n\t",
    "async": true,
    "complexity": "promise"
  },
  {
    "category": "when",
    "name": "provides access to `it` and syncs initial value",
    "html": "<div _=\"when $global changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $global to 'initial'\")\n\t\tawait html(`<div _=\"when $global changes put it into me\"></div>`)\n\t\tawait expect(find('div')).toHaveText('initial')\n\n\t\tawait run(\"set $global to 'hello world'\")\n\t\tawait expect(find('div')).toHaveText('hello world')\n\n\t\tawait run(\"set $global to 42\")\n\t\tawait expect(find('div')).toHaveText('42')\n\n\t\tawait evaluate(() => { delete window.$global })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "rapid detach/reattach in same sync block does not kill effect",
    "html": "<div id=\"thrash-parent\"></div>",
    "body": "\n\t\tawait run(\"set $thrash to 'before'\")\n\t\tawait html(\n\t\t\t`<div id=\"thrash-parent\"></div>`\n\t\t)\n\t\tawait evaluate(() => {\n\t\t\tvar parent = document.getElementById('thrash-parent')\n\t\t\tparent.innerHTML = '<span _=\"when $thrash changes put it into me\"></span>'\n\t\t\t_hyperscript.processNode(parent)\n\t\t})\n\t\tawait expect.poll(() => find('span').textContent()).toBe('before')\n\n\t\t// Detach and immediately reattach in the same synchronous block\n\t\tawait evaluate(() => {\n\t\t\tvar span = document.querySelector('#thrash-parent span')\n\t\t\tvar parent = span.parentNode\n\t\t\tparent.removeChild(span)\n\t\t\tparent.appendChild(span)\n\t\t})\n\n\t\t// Effect should still work\n\t\tawait run(\"set $thrash to 'after'\")\n\t\tawait expect.poll(() => find('span').textContent()).toBe('after')\n\t\tawait evaluate(() => { delete window.$thrash })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "reassigning whole array IS detected",
    "html": "<div _=\"when $arrWhole changes put it.join(',') into me\"></div>",
    "body": "\n\t\tawait run(\"set $arrWhole to [1, 2, 3]\")\n\t\tawait html(`<div _=\"when $arrWhole changes put it.join(',') into me\"></div>`)\n\t\tawait expect.poll(() => find('div').textContent()).toBe('1,2,3')\n\t\tawait run(\"set $arrWhole to [4, 5, 6]\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('4,5,6')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "string template with tracked symbol works",
    "html": "<div _=\"when `hello ${$tplName}` changes put it into me\"></div>",
    "body": "\n\t\tawait run(\"set $tplName to 'world'\")\n\t\tawait html('<div _=\"when `hello ${$tplName}` changes put it into me\"></div>')\n\t\tawait expect(find('div')).toHaveText('hello world')\n\t\tawait run(\"set $tplName to 'there'\")\n\t\tawait expect.poll(() => find('div').textContent()).toBe('hello there')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "supports multiple when features on the same element",
    "html": "<div _=\"when $left changes put it into my @data-left end\n\t\t\t         when $right changes put it into my @data-right\"></div>",
    "body": "\n\t\tawait run(\"set $left to 'L'\")\n\t\tawait run(\"set $right to 'R'\")\n\t\tawait html(\n\t\t\t`<div _=\"when $left changes put it into my @data-left end\n\t\t\t         when $right changes put it into my @data-right\"></div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveAttribute('data-left', 'L')\n\t\tawait expect(find('div')).toHaveAttribute('data-right', 'R')\n\n\t\tawait run(\"set $left to 'newL'\")\n\t\tawait expect(find('div')).toHaveAttribute('data-left', 'newL')\n\t\tawait expect(find('div')).toHaveAttribute('data-right', 'R')\n\t\tawait evaluate(() => { delete window.$left; delete window.$right })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "supports three or more expressions with or",
    "html": "<div _=\"when $r or $g or $b changes put it into me\"></div>",
    "body": "\n\t\tawait html(`<div _=\"when $r or $g or $b changes put it into me\"></div>`)\n\t\tawait run(\"set $r to 'red'\")\n\t\tawait expect(find('div')).toHaveText('red')\n\t\tawait run(\"set $g to 'green'\")\n\t\tawait expect(find('div')).toHaveText('green')\n\t\tawait run(\"set $b to 'blue'\")\n\t\tawait expect(find('div')).toHaveText('blue')\n\t\tawait evaluate(() => { delete window.$r; delete window.$g; delete window.$b })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "triggers multiple elements watching same variable",
    "html": "<div id=\"d1\" _=\"when $shared changes put 'first' into me\"></div><div id=\"d2\" _=\"when $shared changes put 'second' into me\"></div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div id=\"d1\" _=\"when $shared changes put 'first' into me\"></div>` +\n\t\t\t`<div id=\"d2\" _=\"when $shared changes put 'second' into me\"></div>`\n\t\t)\n\t\tawait run(\"set $shared to 'changed'\")\n\t\tawait expect(find('#d1')).toHaveText('first')\n\t\tawait expect(find('#d2')).toHaveText('second')\n\t\tawait evaluate(() => { delete window.$shared })\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "value of #element is tracked",
    "html": "<input type=\"text\" id=\"of-input\" value=\"init\" /><span _=\"when (value of #of-input) changes put it into me\"></span>",
    "body": "\n\t\tawait html(\n\t\t\t`<input type=\"text\" id=\"of-input\" value=\"init\" />` +\n\t\t\t`<span _=\"when (value of #of-input) changes put it into me\"></span>`\n\t\t)\n\t\tawait expect(find('span')).toHaveText('init')\n\t\tawait find('#of-input').fill('changed')\n\t\tawait expect.poll(() => find('span').textContent()).toBe('changed')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "when",
    "name": "works with on handlers that modify the watched variable",
    "html": "<div _=\"init set :label to 'initial' end\n\t\t\t         when :label changes put it into me end\n\t\t\t         on click set :label to 'clicked'\">initial</div>",
    "body": "\n\t\tawait html(\n\t\t\t`<div _=\"init set :label to 'initial' end\n\t\t\t         when :label changes put it into me end\n\t\t\t         on click set :label to 'clicked'\">initial</div>`\n\t\t)\n\t\tawait expect(find('div')).toHaveText('initial')\n\t\tawait find('div').click()\n\t\tawait expect(find('div')).toHaveText('clicked')\n\t",
    "async": true,
    "complexity": "simple"
  },
  {
    "category": "worker",
    "name": "raises a helpful error when the worker plugin is not installed",
    "html": "",
    "body": "\n\t\t// The core bundle only ships a stub; the actual worker plugin is\n\t\t// a separate ext that must be loaded. Without it, parsing should\n\t\t// fail with a message pointing the user to the docs.\n\t\tconst msg = await error(\"worker MyWorker def noop() end end\")\n\t\texpect(msg).toContain('worker plugin')\n\t\texpect(msg).toContain('hyperscript.org/features/worker')\n\t",
    "async": true,
    "complexity": "simple"
  }
]
