Files
MujicaUI/option_chain_test.go
T

131 lines
3.8 KiB
Go

package mujica
import (
"testing"
"github.com/egoist/mygo/ui"
)
func optionChainTester(s *OptionChainState, strikes []OptionStrike, opts OptionChainOptions, got *OptionChainView) *ui.Tester {
tt := ui.NewTester(func(c *ui.Context) {
Use(c, Settings{})
ui.Column(c).Padding(10).Height(420).Children(func() {
v := OptionChain(c, s, []string{"Oct 17", "Nov 21"}, strikes, opts)
if v.Changed || v.Submitted || v.ExpiryChanged {
*got = v
}
})
}, 780, 440)
// Rows show once the list has been laid out.
tt.Frame()
return tt
}
func TestOptionChainChooseByClickAndKeys(t *testing.T) {
var s OptionChainState
var got OptionChainView
tt := optionChainTester(&s, mktStrikes(), OptionChainOptions{Spot: 100.4}, &got)
tt.Frame()
optionChainClick(t, tt, "102.00", OptionPut)
if !s.Chosen || s.Strike != 102 || s.Side != OptionPut || !got.Changed {
t.Fatalf("state %+v", s)
}
tt.Key(0, ui.KeyLeft)
if s.Side != OptionCall || s.Strike != 102 {
t.Fatalf("Left kept side %v", s.Side)
}
tt.Key(0, ui.KeyDown)
if s.Strike != 103 {
t.Fatalf("Down chose %v", s.Strike)
}
tt.Key(0, ui.KeyEnter)
if !got.Submitted {
t.Fatal("Enter did not submit")
}
}
func TestOptionChainMarksInTheMoneyAndCenters(t *testing.T) {
var strikes []OptionStrike
for i := range 300 {
strikes = append(strikes, OptionStrike{Strike: float64(i) + 0.5})
}
var s OptionChainState
var got OptionChainView
tt := optionChainTester(&s, strikes, OptionChainOptions{Spot: 200}, &got)
tt.Frame()
if !tt.HasText("200.50") || tt.HasText("Call 5.50") {
t.Fatalf("not centered on the spot: %q", tt.Texts())
}
if n := len(tt.Texts()); n > 400 {
t.Fatalf("built %d texts for 300 strikes", n)
}
if !tt.HasText("Call 199.50: bid 0.00, ask 0.00, last 0.00, in the money") || tt.HasText("Put 199.50: bid 0.00, ask 0.00, last 0.00, in the money") {
t.Fatal("in-the-money marks wrong")
}
}
func TestOptionChainExpiryAndDisabled(t *testing.T) {
var s OptionChainState
var got OptionChainView
tt := optionChainTester(&s, mktStrikes(), OptionChainOptions{Spot: 100}, &got)
_ = tt.Click("Oct 17")
_ = tt.Click("Nov 21")
if s.Expiry != "Nov 21" || !got.ExpiryChanged {
t.Fatalf("expiry %q", s.Expiry)
}
var d OptionChainState
got = OptionChainView{}
tt = optionChainTester(&d, mktStrikes(), OptionChainOptions{Spot: 100, Disabled: true}, &got)
optionChainClick(t, tt, "102.00", OptionPut)
if d.Chosen || got.Changed {
t.Fatal("disabled chain chose")
}
}
func TestOptionChainRejectsUnsortedStrikes(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatal("no panic")
}
}()
var s OptionChainState
ui.Render(func(c *ui.Context) {
Use(c, Settings{})
OptionChain(c, &s, []string{"x"}, []OptionStrike{{Strike: 2}, {Strike: 1}}, OptionChainOptions{})
}, 100, 100, 1)
}
func TestOptionChainSnapshot(t *testing.T) {
snapshot(t, "option_chain", 760, 400, func(c *ui.Context) {
s := &OptionChainState{Strike: 99, Side: OptionCall, Chosen: true}
ui.Column(c).Padding(16).Height(400).Children(func() {
OptionChain(c, s, []string{"Oct 17", "Nov 21"}, mktStrikes(), OptionChainOptions{Spot: 100.4})
})
})
}
// optionChainClick clicks the call or put half of the row for strike.
func optionChainClick(t *testing.T, tt *ui.Tester, strike string, side OptionSide) {
t.Helper()
r, ok := tt.Find(strike)
if !ok {
t.Fatalf("%q not shown", strike)
}
x := r.X + r.W*0.2
if side == OptionPut {
x = r.X + r.W*0.8
}
tt.ClickAt(x, r.Y+r.H/2)
}
func TestOptionChainSameStrikeOtherSide(t *testing.T) {
var s OptionChainState
var got OptionChainView
tt := optionChainTester(&s, mktStrikes(), OptionChainOptions{Spot: 100}, &got)
optionChainClick(t, tt, "102.00", OptionCall)
optionChainClick(t, tt, "102.00", OptionPut)
if s.Strike != 102 || s.Side != OptionPut {
t.Fatalf("contract %+v", s)
}
}