package app import ( "reflect" "testing" "unicode/utf8" "git.stormux.org/storm/cthulhu/internal/atspi" "git.stormux.org/storm/cthulhu/internal/config" "git.stormux.org/storm/cthulhu/internal/review" ) func TestBrailleContextVerbosityAndRoutingOffsets(t *testing.T) { options := config.DefaultSettings().Braille source := atspi.Snapshot{BusName: ":1.42", Path: "/entry", Role: "entry", Name: "Title", TextCaretKnown: true, BrailleLine: "é猫", BrailleCaret: 1, SelectionStart: 0, SelectionEnd: 1, BrailleContext: []atspi.Snapshot{{Role: "panel", Name: "Details"}, {Role: "dialog", Name: "Properties"}, {Role: "application", Name: "Editor"}}} for _, test := range []struct { verbosity int prefix string }{ {1, "Editor application Properties dialog Details panel "}, {0, "Editor Properties Details "}, } { options.Verbosity = test.verbosity frame := brailleFocusFrameWithOptions(source, options) shift := utf8.RuneCountInString(test.prefix) + 6 if frame.Text != test.prefix+"Title é猫 $l" || frame.Cursor != shift+1 || len(frame.Routes) != 1 || frame.Routes[0].Start != shift || frame.Dots[shift] != byte(options.SelectionIndicator) { t.Fatalf("verbosity %d = %+v", test.verbosity, frame) } } options.Verbosity, options.AbbreviateRoles = 1, true if frame := brailleFocusFrameWithOptions(source, options); frame.Text != "Editor app Properties dlg Details pnl Title é猫 $l" { t.Fatalf("abbreviated context = %q", frame.Text) } source.BrailleLineStart, source.BrailleCaret = 4, 5 if frame := brailleFocusFrameWithOptions(source, options); frame.Text != "Title é猫 $l" { t.Fatalf("later line repeats context: %+v", frame) } source.BrailleContext = append(source.BrailleContext, atspi.Snapshot{Role: "document web"}) source.BrailleLineStart, source.BrailleCaret = 0, 1 if frame := brailleFocusFrameWithOptions(source, options); frame.Text != "Title é猫 $l" { t.Fatalf("web text includes desktop ancestry: %+v", frame) } options.SelectionIndicator, options.LinkIndicator = 0x40, 0x80 source.Role, source.Name = "static", "é猫" source.BrailleContext = []atspi.Snapshot{{Role: "link"}, {Role: "document web"}} if frame := brailleFocusFrameWithOptions(source, options); frame.Text != "é猫 $l" || frame.Dots[0] != 0xc0 || frame.Dots[1] != 0x80 || frame.Dots[2] != 0 { t.Fatalf("native browse link selection = %+v", frame) } } func TestBrailleContextHeadersGroupsAndBriefRoles(t *testing.T) { options := config.DefaultSettings().Braille options.Verbosity = 0 radio := atspi.Snapshot{Role: "radio button", Name: "Brief", RadioGroupName: "Verbosity", BrailleContext: []atspi.Snapshot{{Role: "panel", Name: "Verbosity"}}} if frame := brailleFocusFrameWithOptions(radio, options); frame.Text != "Verbosity & y Brief" { t.Fatalf("radio group = %q", frame.Text) } cell := atspi.Snapshot{Role: "table cell", Name: "10", TableRowHeaders: "January", TableColumnHeaders: "Total"} if frame := brailleFocusFrameWithOptions(cell, options); frame.Text != "January Total 10" { t.Fatalf("table headers = %q", frame.Text) } if frame := brailleFocusFrameWithOptions(atspi.Snapshot{Role: "button"}, options); frame.Text != "" { t.Fatalf("brief unnamed role = %q", frame.Text) } if frame := brailleFocusFrameWithOptions(atspi.Snapshot{Role: "heading", Name: "Title", HeadingLevel: 2}, options); frame.Text != "Title h2" { t.Fatalf("brief heading = %q", frame.Text) } } func TestBrowseBrailleMapsInlineSelectionsAndLinkDots(t *testing.T) { options := config.DefaultSettings().Braille options.SelectionIndicator, options.LinkIndicator = 0x40, 0x80 first := atspi.Snapshot{BusName: ":1.42", Path: "/first", Role: "static", SelectionStart: 1, SelectionEnd: 2} second := atspi.Snapshot{BusName: ":1.42", Path: "/second", Role: "static", SelectionStart: 7, SelectionEnd: 10, BrailleContext: []atspi.Snapshot{{Role: "link"}}} doc := atspi.Snapshot{BusName: first.BusName, Path: "/document", Role: "document web"} tree, err := atspi.NewAccessibleTree(doc.ID(), []atspi.TreeNode{{Snapshot: doc, Children: []atspi.ObjectID{first.ID(), second.ID()}}, {Snapshot: first}, {Snapshot: second}}) if err != nil { t.Fatal(err) } docRef, _ := tree.Reference(doc.ID()) result := review.Result{DocumentEpoch: 3, DocumentFocus: doc.ID(), DocumentPosition: review.DocumentPosition{Document: docRef, Snapshot: second, Offset: 8}, DocumentCaretLine: &atspi.DocumentLine{Contents: []atspi.DocumentContent{{Snapshot: first, Start: 0, End: 2, RawText: "é猫"}, {Snapshot: second, Start: 7, End: 11, RawText: "word", Separator: " "}}}} frame := brailleDocumentFrame(result, options) if frame.Text != "é猫 word" || frame.Cursor != 4 || frame.Document == nil || frame.Document.Epoch != 3 || len(frame.Routes) != 2 || frame.Routes[1].SourceStart != 7 || frame.Routes[1].Start != 3 { t.Fatalf("inline frame = %+v", frame) } if !reflect.DeepEqual(frame.Dots, []byte{0, 0x40, 0, 0xc0, 0xc0, 0xc0, 0x80}) { t.Fatalf("inline dots = %v", frame.Dots) } result.DocumentCaretLine = nil result.DocumentBrailleLine = &atspi.DocumentCaretText{Start: 7, Text: "word\n", SelectionStart: 8, SelectionEnd: 10} result.DocumentPosition.Offset = 11 frame = brailleDocumentFrame(result, options) if frame.Text != "word " || frame.Cursor != 4 || frame.Routes[0].End != 5 || frame.Routes[0].TextLength != 4 { t.Fatalf("end cursor route = %+v", frame) } }