[{"data":1,"prerenderedAt":1195},["ShallowReactive",2],{"navigation_docs":3,"-guide-testing":223,"-guide-testing-surround":1190},[4,26,96,105,202],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F1.installation","i-lucide-download",{"title":16,"path":17,"stem":18,"icon":19},"The first app","\u002Fgetting-started\u002Ffirst-app","1.getting-started\u002F2.first-app","i-lucide-play",{"title":21,"path":22,"stem":23,"icon":24},"The first test","\u002Fgetting-started\u002Ffirst-test","1.getting-started\u002F3.first-test","i-lucide-flask-conical",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Guide","i-lucide-book-open","\u002Fguide","2.guide",[32,37,42,47,52,57,62,67,72,77,81,86,91],{"title":33,"path":34,"stem":35,"icon":36},"Components","\u002Fguide\u002Fcomponents","2.guide\u002F01.components","i-lucide-component",{"title":38,"path":39,"stem":40,"icon":41},"Inputs and forms","\u002Fguide\u002Finputs-and-forms","2.guide\u002F02.inputs-and-forms","i-lucide-text-cursor-input",{"title":43,"path":44,"stem":45,"icon":46},"Scrolling and viewports","\u002Fguide\u002Fscrolling","2.guide\u002F03.scrolling","i-lucide-scroll-text",{"title":48,"path":49,"stem":50,"icon":51},"Overlays and layers","\u002Fguide\u002Foverlays","2.guide\u002F04.overlays","i-lucide-layers",{"title":53,"path":54,"stem":55,"icon":56},"Styles and theme","\u002Fguide\u002Fstyles","2.guide\u002F05.styles","i-lucide-palette",{"title":58,"path":59,"stem":60,"icon":61},"Composables","\u002Fguide\u002Fcomposables","2.guide\u002F06.composables","i-lucide-puzzle",{"title":63,"path":64,"stem":65,"icon":66},"Widgets","\u002Fguide\u002Fwidgets","2.guide\u002F07.widgets","i-lucide-blocks",{"title":68,"path":69,"stem":70,"icon":71},"The assistant","\u002Fguide\u002Fassistant","2.guide\u002F08.assistant","i-lucide-message-square",{"title":73,"path":74,"stem":75,"icon":76},"The same app on the web","\u002Fguide\u002Fweb","2.guide\u002F09.web","i-lucide-globe",{"title":78,"path":79,"stem":80,"icon":24},"Testing","\u002Fguide\u002Ftesting","2.guide\u002F10.testing",{"title":82,"path":83,"stem":84,"icon":85},"Running and packaging","\u002Fguide\u002Fpackaging","2.guide\u002F11.packaging","i-lucide-package",{"title":87,"path":88,"stem":89,"icon":90},"The terminal contract","\u002Fguide\u002Fterminal","2.guide\u002F12.terminal","i-lucide-square-terminal",{"title":92,"path":93,"stem":94,"icon":95},"Benchmarks","\u002Fguide\u002Fbenchmarks","2.guide\u002F13.benchmarks","i-lucide-gauge",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":25},"AI and agents","i-lucide-bot","\u002Fai","3.ai",[102],{"title":97,"path":103,"stem":104,"icon":98},"\u002Fai\u002Fagents","3.ai\u002F1.agents",{"title":106,"icon":107,"path":108,"stem":109,"children":110},"Decisions","i-lucide-scale","\u002Fdecisions","4.decisions",[111,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,194,198],{"title":112,"path":108,"stem":113,"icon":46},"All decisions","4.decisions\u002Findex",{"title":115,"path":116,"stem":117},"0001. kyo-ui is the DSL","\u002Fdecisions\u002F0001-kyo-ui-is-the-dsl","4.decisions\u002F0001-kyo-ui-is-the-dsl",{"title":119,"path":120,"stem":121},"0002. jatatui renders the cells","\u002Fdecisions\u002F0002-jatatui-renders-the-cells","4.decisions\u002F0002-jatatui-renders-the-cells",{"title":123,"path":124,"stem":125},"0003. Pin Kyo 1.0.0-RC7 and JDK 25","\u002Fdecisions\u002F0003-pin-kyo-rc7-and-jdk-25","4.decisions\u002F0003-pin-kyo-rc7-and-jdk-25",{"title":127,"path":128,"stem":129},"0004. Public Kyo APIs only","\u002Fdecisions\u002F0004-public-kyo-apis-only","4.decisions\u002F0004-public-kyo-apis-only",{"title":131,"path":132,"stem":133},"0005. A SOFT frame contract and an own watcher loop","\u002Fdecisions\u002F0005-soft-frame-contract-and-own-watcher-loop","4.decisions\u002F0005-soft-frame-contract-and-own-watcher-loop",{"title":135,"path":136,"stem":137},"0006. Purity: edge files, lint and a pure painter","\u002Fdecisions\u002F0006-purity-edge-files-and-lint","4.decisions\u002F0006-purity-edge-files-and-lint",{"title":139,"path":140,"stem":141},"0007. Compiler flags from sbt-tpolecat","\u002Fdecisions\u002F0007-compiler-flags-from-sbt-tpolecat","4.decisions\u002F0007-compiler-flags-from-sbt-tpolecat",{"title":143,"path":144,"stem":145},"0008. The artifact split: runner, app, testkit and core","\u002Fdecisions\u002F0008-artifact-split-runner-app-testkit-core","4.decisions\u002F0008-artifact-split-runner-app-testkit-core",{"title":147,"path":148,"stem":149},"0009. tui4s-use and tui4s-widgets, cross-built","\u002Fdecisions\u002F0009-use-and-widgets-cross-built","4.decisions\u002F0009-use-and-widgets-cross-built",{"title":151,"path":152,"stem":153},"0010. The flagship assistant is the blueprint of a real app","\u002Fdecisions\u002F0010-the-flagship-assistant-as-blueprint","4.decisions\u002F0010-the-flagship-assistant-as-blueprint",{"title":155,"path":156,"stem":157},"0011. The web target: a standalone Scala.js app and one theme","\u002Fdecisions\u002F0011-web-target-standalone-scalajs-and-one-theme","4.decisions\u002F0011-web-target-standalone-scalajs-and-one-theme",{"title":159,"path":160,"stem":161},"0012. The documentation is example-first","\u002Fdecisions\u002F0012-documentation-is-example-first","4.decisions\u002F0012-documentation-is-example-first",{"title":163,"path":164,"stem":165},"0013. Benchmarks in the repository, with their own build","\u002Fdecisions\u002F0013-benchmarks-in-repo-with-own-build","4.decisions\u002F0013-benchmarks-in-repo-with-own-build",{"title":167,"path":168,"stem":169},"0014. Release per module, by tag, as 0.1.0-RC1","\u002Fdecisions\u002F0014-release-per-module-tags-and-rc1","4.decisions\u002F0014-release-per-module-tags-and-rc1",{"title":171,"path":172,"stem":173},"0015. Packaging: the AOT cache in the start scripts, native-image as an option","\u002Fdecisions\u002F0015-packaging-aot-cache-and-native-image","4.decisions\u002F0015-packaging-aot-cache-and-native-image",{"title":175,"path":176,"stem":177},"0016. Process: lean mode and agent roles","\u002Fdecisions\u002F0016-process-lean-mode-and-agent-roles","4.decisions\u002F0016-process-lean-mode-and-agent-roles",{"title":179,"path":180,"stem":181},"0017. Deferred: templates, mouse, the kitty keyboard protocol and Windows","\u002Fdecisions\u002F0017-deferred-templates-mouse-and-kitty","4.decisions\u002F0017-deferred-templates-mouse-and-kitty",{"title":183,"path":184,"stem":185},"0018. pini4s is a vendored Git submodule","\u002Fdecisions\u002F0018-pini4s-as-a-vendored-submodule","4.decisions\u002F0018-pini4s-as-a-vendored-submodule",{"title":187,"path":188,"stem":189},"0019. Upstream issues are drafts that the user posts","\u002Fdecisions\u002F0019-upstream-kyo-issues-as-drafts","4.decisions\u002F0019-upstream-kyo-issues-as-drafts",{"title":191,"path":192,"stem":193},"0020. Release from the Actions form","\u002Fdecisions\u002F0020-release-from-the-actions-form","4.decisions\u002F0020-release-from-the-actions-form",{"title":195,"path":196,"stem":197},"0021. The rulebook is CONTRIBUTING.md","\u002Fdecisions\u002F0021-the-rulebook-is-contributing-md","4.decisions\u002F0021-the-rulebook-is-contributing-md",{"title":199,"path":200,"stem":201},"0022. The logo is text","\u002Fdecisions\u002F0022-the-logo-is-text","4.decisions\u002F0022-the-logo-is-text",{"title":203,"icon":204,"path":205,"stem":206,"children":207,"page":25},"Project","i-lucide-compass","\u002Fproject","5.project",[208,213,218],{"title":209,"path":210,"stem":211,"icon":212},"Roadmap","\u002Fproject\u002Froadmap","5.project\u002F1.roadmap","i-lucide-map",{"title":214,"path":215,"stem":216,"icon":217},"Contributing","\u002Fproject\u002Fcontributing","5.project\u002F2.contributing","i-lucide-git-pull-request",{"title":219,"path":220,"stem":221,"icon":222},"Security","\u002Fproject\u002Fsecurity","5.project\u002F3.security","i-lucide-shield",{"id":224,"title":78,"body":225,"description":1183,"extension":1184,"links":1185,"meta":1186,"navigation":1187,"path":79,"seo":1188,"stem":80,"__hash__":1189},"docs\u002F2.guide\u002F10.testing.md",{"type":226,"value":227,"toc":1178},"minimark",[228,943,958,963,983,1107,1114,1118,1134,1138,1145,1156,1174],[229,230,236],"pre",{"className":231,"code":232,"filename":233,"language":234,"meta":235,"style":235},"language-scala shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","    \u002F** Runs `v` to its end on the Kyo scheduler (at most 60 s). *\u002F\n    private def run[A](v: => A \u003C (Async & Scope))(using Frame): A =\n        import AllowUnsafe.embrace.danger\n        KyoApp.Unsafe.runAndBlock(60.seconds)(v).getOrThrow\n\n    private def start(ui: => UI \u003C (Async & Scope))(using\n        Frame,\n    ): Headless \u003C (Async & Scope) =\n      Headless.start(60, 10)(_ => ui)\n\n    \u002F** Waits until the screen shows `text`, at most 10 s. *\u002F\n    private def shows(h: Headless, text: String)(using Frame): Boolean \u003C Async =\n      h.awaitScreen(_.contains(text), 10.seconds)\n\n    test(\n      \"search: the results of the quiet query; a new query gives new results\",\n    ) {\n      val ok = run {\n        for\n            h     \u003C- start(Recipes.search(q => Chunk(s\"$q-1\", s\"$q-2\")))\n            idle  \u003C- shows(h, \"type to search\")\n            _     \u003C- h.typeText(\"kyo\")\n            found \u003C- shows(h, \"found: kyo-1, kyo-2\")\n            _     \u003C- h.key(Keyboard.Backspace)\n            again \u003C- shows(h, \"found: ky-1, ky-2\")\n        yield (idle, found, again)\n      }\n      assertEquals(ok, (true, true, true))\n    }\n\n    test(\"progress: 100 writes in a row; the last value shows\") {\n      val ok = run {\n        for\n            done \u003C- Signal.initRef(0)\n            h    \u003C- start(Recipes.progress(done))\n            _    \u003C- Kyo.foreach(1 to 100)(done.set)\n            last \u003C- shows(h, \"100 %\")\n        yield last\n      }\n      assert(ok)\n    }\n","RecipesSuite.scala","scala","",[237,238,239,248,322,343,363,370,408,417,444,469,474,480,531,542,547,553,568,577,594,601,660,681,701,720,740,759,768,774,794,800,805,822,835,840,859,874,900,919,927,932,938],"code",{"__ignoreMap":235},[240,241,244],"span",{"class":242,"line":243},"line",1,[240,245,247],{"class":246},"sHwdD","    \u002F** Runs `v` to its end on the Kyo scheduler (at most 60 s). *\u002F\n",[240,249,251,255,259,263,267,271,274,278,281,284,287,290,293,296,299,302,305,308,311,314,317,319],{"class":242,"line":250},2,[240,252,254],{"class":253},"spNyl","    private",[240,256,258],{"class":257},"sMK4o"," def",[240,260,262],{"class":261},"s2Zo4"," run",[240,264,266],{"class":265},"sTEyZ","[",[240,268,270],{"class":269},"sBMFI","A",[240,272,273],{"class":265},"](",[240,275,277],{"class":276},"sHdIc","v",[240,279,280],{"class":265},": ",[240,282,283],{"class":257},"=>",[240,285,286],{"class":269}," A",[240,288,289],{"class":257}," \u003C",[240,291,292],{"class":265}," (",[240,294,295],{"class":269},"Async",[240,297,298],{"class":257}," &",[240,300,301],{"class":269}," Scope",[240,303,304],{"class":265},"))(",[240,306,307],{"class":257},"using",[240,309,310],{"class":269}," Frame",[240,312,313],{"class":265},")",[240,315,316],{"class":257},":",[240,318,286],{"class":269},[240,320,321],{"class":257}," =\n",[240,323,325,329,332,335,338,340],{"class":242,"line":324},3,[240,326,328],{"class":327},"sbssI","        import",[240,330,331],{"class":269}," AllowUnsafe",[240,333,334],{"class":257},".",[240,336,337],{"class":269},"embrace",[240,339,334],{"class":257},[240,341,342],{"class":269},"danger\n",[240,344,346,349,351,354,357,360],{"class":242,"line":345},4,[240,347,348],{"class":269},"        KyoApp",[240,350,334],{"class":265},[240,352,353],{"class":269},"Unsafe",[240,355,356],{"class":265},".runAndBlock(",[240,358,359],{"class":327},"60",[240,361,362],{"class":265},".seconds)(v).getOrThrow\n",[240,364,366],{"class":242,"line":365},5,[240,367,369],{"emptyLinePlaceholder":368},true,"\n",[240,371,373,375,377,380,383,386,388,390,393,395,397,399,401,403,405],{"class":242,"line":372},6,[240,374,254],{"class":253},[240,376,258],{"class":257},[240,378,379],{"class":261}," start",[240,381,382],{"class":265},"(",[240,384,385],{"class":276},"ui",[240,387,280],{"class":265},[240,389,283],{"class":257},[240,391,392],{"class":269}," UI",[240,394,289],{"class":257},[240,396,292],{"class":265},[240,398,295],{"class":269},[240,400,298],{"class":257},[240,402,301],{"class":269},[240,404,304],{"class":265},[240,406,407],{"class":257},"using\n",[240,409,411,414],{"class":242,"line":410},7,[240,412,413],{"class":269},"        Frame",[240,415,416],{"class":265},",\n",[240,418,420,423,425,428,430,432,434,436,438,441],{"class":242,"line":419},8,[240,421,422],{"class":265},"    )",[240,424,316],{"class":257},[240,426,427],{"class":269}," Headless",[240,429,289],{"class":257},[240,431,292],{"class":265},[240,433,295],{"class":269},[240,435,298],{"class":257},[240,437,301],{"class":269},[240,439,440],{"class":265},") ",[240,442,443],{"class":257},"=\n",[240,445,447,450,453,455,458,461,464,466],{"class":242,"line":446},9,[240,448,449],{"class":269},"      Headless",[240,451,452],{"class":265},".start(",[240,454,359],{"class":327},[240,456,457],{"class":265},", ",[240,459,460],{"class":327},"10",[240,462,463],{"class":265},")(_ ",[240,465,283],{"class":257},[240,467,468],{"class":265}," ui)\n",[240,470,472],{"class":242,"line":471},10,[240,473,369],{"emptyLinePlaceholder":368},[240,475,477],{"class":242,"line":476},11,[240,478,479],{"class":246},"    \u002F** Waits until the screen shows `text`, at most 10 s. *\u002F\n",[240,481,483,485,487,490,492,495,497,500,502,505,507,510,513,515,517,519,521,524,526,529],{"class":242,"line":482},12,[240,484,254],{"class":253},[240,486,258],{"class":257},[240,488,489],{"class":261}," shows",[240,491,382],{"class":265},[240,493,494],{"class":276},"h",[240,496,280],{"class":265},[240,498,499],{"class":269},"Headless",[240,501,457],{"class":265},[240,503,504],{"class":276},"text",[240,506,280],{"class":265},[240,508,509],{"class":269},"String",[240,511,512],{"class":265},")(",[240,514,307],{"class":257},[240,516,310],{"class":269},[240,518,313],{"class":265},[240,520,316],{"class":257},[240,522,523],{"class":269}," Boolean",[240,525,289],{"class":257},[240,527,528],{"class":269}," Async",[240,530,321],{"class":257},[240,532,534,537,539],{"class":242,"line":533},13,[240,535,536],{"class":265},"      h.awaitScreen(_.contains(text), ",[240,538,460],{"class":327},[240,540,541],{"class":265},".seconds)\n",[240,543,545],{"class":242,"line":544},14,[240,546,369],{"emptyLinePlaceholder":368},[240,548,550],{"class":242,"line":549},15,[240,551,552],{"class":265},"    test(\n",[240,554,556,559,563,566],{"class":242,"line":555},16,[240,557,558],{"class":257},"      \"",[240,560,562],{"class":561},"sfazB","search: the results of the quiet query; a new query gives new results",[240,564,565],{"class":257},"\"",[240,567,416],{"class":265},[240,569,571,574],{"class":242,"line":570},17,[240,572,573],{"class":265},"    ) ",[240,575,576],{"class":257},"{\n",[240,578,580,583,586,589,592],{"class":242,"line":579},18,[240,581,582],{"class":257},"      val",[240,584,585],{"class":265}," ok ",[240,587,588],{"class":257},"=",[240,590,591],{"class":265}," run ",[240,593,576],{"class":257},[240,595,597],{"class":242,"line":596},19,[240,598,600],{"class":599},"s7zQu","        for\n",[240,602,604,607,610,613,616,619,621,624,626,629,631,634,637,640,642,644,646,648,650,652,655,657],{"class":242,"line":603},20,[240,605,606],{"class":265},"            h     ",[240,608,609],{"class":257},"\u003C-",[240,611,612],{"class":265}," start(",[240,614,615],{"class":269},"Recipes",[240,617,618],{"class":265},".search(q ",[240,620,283],{"class":257},[240,622,623],{"class":269}," Chunk",[240,625,382],{"class":265},[240,627,628],{"class":257},"s",[240,630,565],{"class":257},[240,632,633],{"class":257},"$",[240,635,636],{"class":265},"q",[240,638,639],{"class":561},"-1",[240,641,565],{"class":257},[240,643,457],{"class":265},[240,645,628],{"class":257},[240,647,565],{"class":257},[240,649,633],{"class":257},[240,651,636],{"class":265},[240,653,654],{"class":561},"-2",[240,656,565],{"class":257},[240,658,659],{"class":265},")))\n",[240,661,663,666,668,671,673,676,678],{"class":242,"line":662},21,[240,664,665],{"class":265},"            idle  ",[240,667,609],{"class":257},[240,669,670],{"class":265}," shows(h, ",[240,672,565],{"class":257},[240,674,675],{"class":561},"type to search",[240,677,565],{"class":257},[240,679,680],{"class":265},")\n",[240,682,684,687,689,692,694,697,699],{"class":242,"line":683},22,[240,685,686],{"class":265},"            _     ",[240,688,609],{"class":257},[240,690,691],{"class":265}," h.typeText(",[240,693,565],{"class":257},[240,695,696],{"class":561},"kyo",[240,698,565],{"class":257},[240,700,680],{"class":265},[240,702,704,707,709,711,713,716,718],{"class":242,"line":703},23,[240,705,706],{"class":265},"            found ",[240,708,609],{"class":257},[240,710,670],{"class":265},[240,712,565],{"class":257},[240,714,715],{"class":561},"found: kyo-1, kyo-2",[240,717,565],{"class":257},[240,719,680],{"class":265},[240,721,723,725,727,730,733,735,738],{"class":242,"line":722},24,[240,724,686],{"class":265},[240,726,609],{"class":257},[240,728,729],{"class":265}," h.key(",[240,731,732],{"class":269},"Keyboard",[240,734,334],{"class":265},[240,736,737],{"class":269},"Backspace",[240,739,680],{"class":265},[240,741,743,746,748,750,752,755,757],{"class":242,"line":742},25,[240,744,745],{"class":265},"            again ",[240,747,609],{"class":257},[240,749,670],{"class":265},[240,751,565],{"class":257},[240,753,754],{"class":561},"found: ky-1, ky-2",[240,756,565],{"class":257},[240,758,680],{"class":265},[240,760,762,765],{"class":242,"line":761},26,[240,763,764],{"class":599},"        yield",[240,766,767],{"class":265}," (idle, found, again)\n",[240,769,771],{"class":242,"line":770},27,[240,772,773],{"class":257},"      }\n",[240,775,777,780,783,785,787,789,791],{"class":242,"line":776},28,[240,778,779],{"class":265},"      assertEquals(ok, (",[240,781,782],{"class":257},"true",[240,784,457],{"class":265},[240,786,782],{"class":257},[240,788,457],{"class":265},[240,790,782],{"class":257},[240,792,793],{"class":265},"))\n",[240,795,797],{"class":242,"line":796},29,[240,798,799],{"class":257},"    }\n",[240,801,803],{"class":242,"line":802},30,[240,804,369],{"emptyLinePlaceholder":368},[240,806,808,811,813,816,818,820],{"class":242,"line":807},31,[240,809,810],{"class":265},"    test(",[240,812,565],{"class":257},[240,814,815],{"class":561},"progress: 100 writes in a row; the last value shows",[240,817,565],{"class":257},[240,819,440],{"class":265},[240,821,576],{"class":257},[240,823,825,827,829,831,833],{"class":242,"line":824},32,[240,826,582],{"class":257},[240,828,585],{"class":265},[240,830,588],{"class":257},[240,832,591],{"class":265},[240,834,576],{"class":257},[240,836,838],{"class":242,"line":837},33,[240,839,600],{"class":599},[240,841,843,846,848,851,854,857],{"class":242,"line":842},34,[240,844,845],{"class":265},"            done ",[240,847,609],{"class":257},[240,849,850],{"class":269}," Signal",[240,852,853],{"class":265},".initRef(",[240,855,856],{"class":327},"0",[240,858,680],{"class":265},[240,860,862,865,867,869,871],{"class":242,"line":861},35,[240,863,864],{"class":265},"            h    ",[240,866,609],{"class":257},[240,868,612],{"class":265},[240,870,615],{"class":269},[240,872,873],{"class":265},".progress(done))\n",[240,875,877,880,882,885,888,891,894,897],{"class":242,"line":876},36,[240,878,879],{"class":265},"            _    ",[240,881,609],{"class":257},[240,883,884],{"class":269}," Kyo",[240,886,887],{"class":265},".foreach(",[240,889,890],{"class":327},"1",[240,892,893],{"class":265}," to ",[240,895,896],{"class":327},"100",[240,898,899],{"class":265},")(done.set)\n",[240,901,903,906,908,910,912,915,917],{"class":242,"line":902},37,[240,904,905],{"class":265},"            last ",[240,907,609],{"class":257},[240,909,670],{"class":265},[240,911,565],{"class":257},[240,913,914],{"class":561},"100 %",[240,916,565],{"class":257},[240,918,680],{"class":265},[240,920,922,924],{"class":242,"line":921},38,[240,923,764],{"class":599},[240,925,926],{"class":265}," last\n",[240,928,930],{"class":242,"line":929},39,[240,931,773],{"class":257},[240,933,935],{"class":242,"line":934},40,[240,936,937],{"class":265},"      assert(ok)\n",[240,939,941],{"class":242,"line":940},41,[240,942,799],{"class":257},[944,945,946,949,950,953,954,957],"p",{},[237,947,948],{},"run"," runs a Kyo value to its end inside a munit test. ",[237,951,952],{},"start"," starts the app on the headless test kit, on a screen of 60 by 10 cells. The first test types into the search box and waits for the results. The second test writes a ",[237,955,956],{},"SignalRef"," of the app 100 times and waits until the screen shows the last value.",[959,960,962],"h2",{"id":961},"the-headless-test-kit","The headless test kit",[944,964,965,968,969,972,973,975,976,978,979,982],{},[237,966,967],{},"tui4s.testkit.Headless"," runs an app with no terminal. ",[237,970,971],{},"Headless.start(width, height)(ui)"," gives a running app. Its ",[237,974,385],{}," gets the quit action, as the ",[237,977,385],{}," of ",[237,980,981],{},"Tui.run"," does.",[984,985,986,999],"table",{},[987,988,989],"thead",{},[990,991,992,996],"tr",{},[993,994,995],"th",{},"Method",[993,997,998],{},"What it does",[1000,1001,1002,1022,1035,1045,1055,1065,1084,1094],"tbody",{},[990,1003,1004,1019],{},[1005,1006,1007,457,1010,457,1013,457,1016],"td",{},[237,1008,1009],{},"press(event)",[237,1011,1012],{},"key(k)",[237,1014,1015],{},"key(k, mods)",[237,1017,1018],{},"char(c)",[1005,1020,1021],{},"Sends one key, and waits for its frame.",[990,1023,1024,1029],{},[1005,1025,1026],{},[237,1027,1028],{},"typeText(s)",[1005,1030,1031,1032,1034],{},"Sends each character of ",[237,1033,628],{}," as a key.",[990,1036,1037,1042],{},[1005,1038,1039],{},[237,1040,1041],{},"paste(text)",[1005,1043,1044],{},"Sends a bracketed paste: one edit.",[990,1046,1047,1052],{},[1005,1048,1049],{},[237,1050,1051],{},"resize(w, h)",[1005,1053,1054],{},"Changes the size of the screen.",[990,1056,1057,1062],{},[1005,1058,1059],{},[237,1060,1061],{},"screen",[1005,1063,1064],{},"Gives the current screen. It does not wait.",[990,1066,1067,1072],{},[1005,1068,1069],{},[237,1070,1071],{},"awaitScreen(p, timeout)",[1005,1073,1074,1075,1077,1078,1081,1082,334],{},"Waits until the screen satisfies ",[237,1076,944],{},", at most ",[237,1079,1080],{},"timeout",". Use it after a write to a ",[237,1083,956],{},[990,1085,1086,1091],{},[1005,1087,1088],{},[237,1089,1090],{},"awaitLog",[1005,1092,1093],{},"Gives the lines of the log of the run.",[990,1095,1096,1104],{},[1005,1097,1098,457,1101],{},[237,1099,1100],{},"finished",[237,1102,1103],{},"awaitEnd(timeout)",[1005,1105,1106],{},"Tells if the run has ended.",[944,1108,1109,1110,1113],{},"A key is one event: its handlers run before the frame, so the frame of the key holds all of their writes. A write from the test is not a key, so the test waits for the screen with ",[237,1111,1112],{},"awaitScreen",". The kit itself never waits unless a method name says so. After the end of the run, a method that waits for a frame fails at once.",[959,1115,1117],{"id":1116},"kyo-test-and-munit","kyo-test and munit",[944,1119,1120,1121,457,1124,457,1127,1130,1131,1133],{},"The examples use munit. tui4s uses kyo-test for its own suites, and kyo-test-prop for the property tests (",[237,1122,1123],{},"PropertyTest",[237,1125,1126],{},"Gen",[237,1128,1129],{},"Shrink",", with explicit ",[237,1132,1126],{}," values). The kit gives Kyo values, so it works with both.",[959,1135,1137],{"id":1136},"the-real-terminal","The real terminal",[944,1139,1140,1141,1144],{},"The PTY kit runs the examples in a pseudo-terminal, with no human. It sends keys, checks raw mode from outside while the app runs, compares ",[237,1142,1143],{},"stty -a"," before and after each run, and checks the screens:",[229,1146,1150],{"className":1147,"code":1148,"language":1149,"meta":235,"style":235},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","examples\u002Fptycheck\u002Frun.sh\n","sh",[237,1151,1152],{"__ignoreMap":235},[240,1153,1154],{"class":242,"line":243},[240,1155,1148],{},[944,1157,1158,1159,457,1162,1165,1166,1169,1170,1173],{},"It needs ",[237,1160,1161],{},"expect",[237,1163,1164],{},"python3"," and JDK 25. CI runs it in the ",[237,1167,1168],{},"terminal"," job. The terminal casts on these pages come from the same pseudo-terminal runs (",[237,1171,1172],{},"scripts\u002Fdocs-casts.sh",").",[1175,1176,1177],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":235,"searchDepth":250,"depth":250,"links":1179},[1180,1181,1182],{"id":961,"depth":250,"text":962},{"id":1116,"depth":250,"text":1117},{"id":1136,"depth":250,"text":1137},"Test an app on the headless test kit, and check the real terminal with the PTY kit.","md",null,{},{"icon":24},{"title":78,"description":1183},"3tTqE6K7anKPOVgEs1nUE1emup0pcQBfyu6tUKld9Hg",[1191,1193],{"title":73,"path":74,"stem":75,"description":1192,"icon":76,"children":-1},"The assistant in a browser, from the same shared code, as static files.",{"title":82,"path":83,"stem":84,"description":1194,"icon":85,"children":-1},"The start scripts, the JVM flags, the AOT cache, a native binary, the options of a TuiApp, the exit codes and the signals.",1792528752826]