1package ui23import (4 "strings"5 "testing"67 tea "charm.land/bubbletea/v2"89 "src.rest/cat/012/internal/sheet"10)1112// Pastes and fills meet the validation of the cells they write, as in13// Sheets: a rule that rejects takes the whole change back, one that warns14// keeps it and says so.15func TestValidationOnPastesAndFills(t *testing.T) {16 setup := func(t *testing.T) *Model {17 m := newModel()18 mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"],"reject":true}`)19 mustValidate(t, m, `{"ranges":"C1:C9","criteria":"number","condition":"between","values":["1","10"]}`)20 m.sheet.Set(addr("A1"), "5")21 m.sheet.Set(addr("A2"), "50")22 return m23 }2425 t.Run("paste rejected", func(t *testing.T) {26 m := setup(t)27 press(t, m, "<shift+down>", "<ctrl+c>", "<right>", "<ctrl+v>")28 if m.mode != modeError || input(m, "B1") != "" || input(m, "B2") != "" || m.sheet.CanRedo() {29 t.Fatalf("mode %v B1 %q B2 %q", m.mode, input(m, "B1"), input(m, "B2"))30 }31 if m.errMsg != "Paste undone: Invalid entry in B2: Input must be a number between 1 and 10" {32 t.Errorf("reason %q", m.errMsg)33 }34 })3536 t.Run("paste warned", func(t *testing.T) {37 m := setup(t)38 press(t, m, "<shift+down>", "<ctrl+c>", "<right>", "<right>", "<ctrl+v>")39 if input(m, "C2") != "50" || !m.sheet.Look(addr("C2")).Invalid {40 t.Fatalf("C2 %q", input(m, "C2"))41 }42 if l := line(m, contextLine); !strings.Contains(l, "1 cell invalid, first C2: Input must be a number between 1 and 10") {43 t.Errorf("context line %q", l)44 }45 })4647 t.Run("text paste rejected", func(t *testing.T) {48 m := setup(t)49 m.cur = addr("B1")50 send(m, tea.PasteMsg{Content: "3\n30"})51 if m.mode != modeError || input(m, "B1") != "" {52 t.Errorf("mode %v B1 %q", m.mode, input(m, "B1"))53 }54 })5556 t.Run("fill down rejected", func(t *testing.T) {57 m := setup(t)58 m.sheet.Set(addr("B1"), "7")59 m.sheet.Set(addr("B1"), "=A1*2") // 10, valid; filled down it's 10060 press(t, m, "<right>", "<shift+down>", "<ctrl+d>")61 if m.mode != modeError || input(m, "B2") != "" {62 t.Errorf("mode %v B2 %q", m.mode, input(m, "B2"))63 }64 })6566 t.Run("ctrl+enter rejected keeps the entry", func(t *testing.T) {67 m := setup(t)68 press(t, m, "<right>", "<shift+down>", "=A1*2", "<ctrl+enter>")69 if m.mode != modeEdit || input(m, "B1") != "" || !strings.Contains(line(m, contextLine), "Invalid entry in B2") {70 t.Errorf("mode %v B1 %q %q", m.mode, input(m, "B1"), line(m, contextLine))71 }72 })73}7475// Cut and paste takes the cells' rules along.76func TestRulesMoveWithCutAndPaste(t *testing.T) {77 m := newModel()78 mustValidate(t, m, `{"ranges":"A1:A2","criteria":"list","items":["Yes","No"]}`)79 m.sheet.Set(addr("A1"), "Yes")80 press(t, m, "<shift+down>", "<ctrl+x>", "<right>", "<right>", "<ctrl+v>")81 vs := m.sheet.Validations()82 if len(vs) != 1 || sheet.RangesText(vs[0].Ranges) != "C1:C2" || !m.sheet.Look(addr("C1")).Dropdown {83 t.Fatalf("validations %+v", vs)84 }85 if m.mode == modeError {86 t.Errorf("a move was refused: %s", screen(m))87 }88}8990// A macro's pastes and fills meet validation as the keys' do: a rule91// that rejects takes that paste or fill back and fails the script with92// the reason, leaving what the script did before it; a rule that warns93// keeps it.94func TestValidationOnMacroPastesAndFills(t *testing.T) {95 m := newModel()96 mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"],"reject":true}`)97 script(t, m, `set("A1", 5)98select("B1:B3")99enter("=A1*2", fill=True)`)100 if !strings.Contains(m.warn, "Fill undone: Invalid entry in B2") || input(m, "B1") != "" || m.sheet.Value(addr("A1")).Num != 5 {101 t.Errorf("fill: warn %q, B1 %q, A1 %v", m.warn, input(m, "B1"), m.sheet.Value(addr("A1")))102 }103 m = newModel()104 mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"],"reject":true}`)105 script(t, m, `set("A1", 5)106set("A2", 50)107set_formula("B1", "A1*2")108select("B1")109fill(rows=2)`)110 if !strings.Contains(m.warn, "Fill undone: Invalid entry in B2") || input(m, "B2") != "" || input(m, "B1") != "=A1*2" {111 t.Errorf("series: warn %q, B2 %q", m.warn, input(m, "B2"))112 }113 m = newModel()114 mustValidate(t, m, `{"ranges":"B1:B9","criteria":"number","condition":"between","values":["1","10"],"reject":true}`)115 script(t, m, `select("B1")116paste_text("3\n30")`)117 if !strings.Contains(m.warn, "Paste undone") || input(m, "B1") != "" {118 t.Errorf("paste: warn %q, B1 %q", m.warn, input(m, "B1"))119 }120}