nandi/frqpublic Fork 0
43a02c2ddd7ebb7cdcd7d46f8c9a20b5b1b55b5e
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Nim owns the screen, Dart owns the pixels 43a02c2 · on 43a02c2ddd7ebb7cdcd7d46f8c9a20b5b1b55b5e · nandi · 18h ago
tui.nim · 105 lines · 3.8 KBNim Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
## The screen as a pure function of the app, and the reducer that moves it.
import std/sequtils
##
## These are the tests the Clojure screens never had and could not easily
## have: a screen there is hiccup over cells a host installs, so exercising
## one means standing up a host. Here it is a function from a record to a
## tree, and a test is a call.

import std/[json, strutils]
import std/unittest
import frq/[ui, state]
import frq/screens/connect as cs

proc find(node: Node, tag: string): seq[Node] =
  ## Every node with this tag, depth first.
  if node.isNil: return
  if node.tag == tag: result.add node
  for c in node.children:
    result.add c.find(tag)

proc texts(node: Node, tag: string): seq[string] =
  for n in node.find(tag):
    result.add n.props{"label"}.getStr()

suite "the connect screen":
  setup:
    app = initState()

  test "renders a page with the title and the server fields":
    let t = cs.connectScreen(app)
    check t.tag == "page"
    check "frq" in t.texts("title")
    check "Server" in t.texts("label")
    check t.find("checkbutton").len == 1

  test "is pure — twice with no dispatch is the same tree":
    check $cs.connectScreen(app).toJson == $cs.connectScreen(app).toJson

  test "guest is the default mode and shows a nickname field":
    let keys = cs.connectScreen(app).find("entry").mapIt(it.props{"key"}.getStr())
    check "nick" in keys
    check "handle" notin keys

  test "the selected mode is the primary button, and only it":
    let t = cs.connectScreen(app)
    var primary: seq[string]
    for b in t.find("button"):
      if b.props{"kind"}.getStr() == "primary":
        primary.add b.props{"label"}.getStr()
    # Guest is selected; Connect is primary because it is the action.
    check "Guest" in primary
    check "Bluesky" notin primary

suite "dispatch":
  setup:
    app = initState()

  test "switching mode changes which fields are shown":
    dispatch(%*{"id": "mode.bluesky"})
    let keys = cs.connectScreen(app).find("entry").mapIt(it.props{"key"}.getStr())
    check "handle" in keys
    check "nick" notin keys

  test "typing into the host field lands in the tree":
    dispatch(%*{"id": "host.change", "value": "localhost"})
    let host = cs.connectScreen(app).find("entry").filterIt(
      it.props{"key"}.getStr() == "host")[0]
    check host.props{"text"}.getStr() == "localhost"

  test "the TLS tick carries the port with it":
    check app.formPort == "6697"
    dispatch(%*{"id": "tls.toggle"})
    check not app.formTls
    check app.formPort == "6667"
    dispatch(%*{"id": "tls.toggle"})
    check app.formPort == "6697"

  test "connecting swaps the button for a spinner":
    check cs.connectScreen(app).find("spinner").len == 0
    dispatch(%*{"id": "connect"})
    let t = cs.connectScreen(app)
    check t.find("spinner").len == 1
    check "Connect" notin t.texts("button")

  test "an empty host is refused, and the error is dismissable":
    dispatch(%*{"id": "host.change", "value": "  "})
    dispatch(%*{"id": "connect"})
    check app.hasError
    check "Dismiss" in cs.connectScreen(app).texts("button")
    dispatch(%*{"id": "error.dismiss"})
    check not app.hasError
    check "Dismiss" notin cs.connectScreen(app).texts("button")

  test "the error note keeps its place in the tree either way":
    # The bug the stable wrapper exists for: a renderer matching children by
    # position would patch the header into a card when the error appeared.
    let before = cs.connectScreen(app).children.mapIt(it.tag)
    dispatch(%*{"id": "host.change", "value": ""})
    dispatch(%*{"id": "connect"})
    check cs.connectScreen(app).children.mapIt(it.tag) == before

  test "an unknown event is ignored rather than fatal":
    let before = $cs.connectScreen(app).toJson
    dispatch(%*{"id": "no.such.event"})
    check $cs.connectScreen(app).toJson == before