mirror of
https://github.com/BishopFox/sliver
synced 2026-06-08 10:29:05 +00:00
200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
package docs
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
"github.com/charmbracelet/x/ansi"
|
|
)
|
|
|
|
func sampleDocs() []docEntry {
|
|
return []docEntry{
|
|
{
|
|
Name: "Getting Started",
|
|
Content: "# Getting Started\n\n- first step\n- second step",
|
|
Description: "Getting Started",
|
|
},
|
|
{
|
|
Name: "Operators",
|
|
Content: "# Operators\n\nTeam workflows",
|
|
Description: "Team workflows",
|
|
},
|
|
{
|
|
Name: "Networking",
|
|
Content: "# Networking\n\nTransport details",
|
|
Description: "Transport details",
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestRenderMarkdownWithGlowRendersBulletLists(t *testing.T) {
|
|
rendered, err := renderMarkdownWithGlow(48, "# Title\n\n- first item")
|
|
if err != nil {
|
|
t.Fatalf("renderMarkdownWithGlow(): %v", err)
|
|
}
|
|
|
|
plain := ansi.Strip(rendered)
|
|
if !strings.Contains(plain, "Title") {
|
|
t.Fatalf("expected heading text, got %q", plain)
|
|
}
|
|
if !strings.Contains(plain, "• first item") {
|
|
t.Fatalf("expected glow-style bullet rendering, got %q", plain)
|
|
}
|
|
}
|
|
|
|
func TestSummarizeMarkdownPrefersFirstMeaningfulLine(t *testing.T) {
|
|
summary := summarizeMarkdown("# Title\n\n- Bullet\n\nParagraph")
|
|
if summary != "Title" {
|
|
t.Fatalf("expected first heading summary, got %q", summary)
|
|
}
|
|
}
|
|
|
|
func TestSummarizeMarkdownStripsMarkdownAndUnicodeSymbols(t *testing.T) {
|
|
summary := summarizeMarkdown("⚠️ **IMPORTANT:** This content was NOT created by the Sliver authors.")
|
|
if summary != "IMPORTANT: This content was NOT created by the Sliver authors." {
|
|
t.Fatalf("expected sanitized browser summary, got %q", summary)
|
|
}
|
|
}
|
|
|
|
func TestDocsModelDefaultsToGettingStarted(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 32)
|
|
|
|
if model.currentDocName != "Getting Started" {
|
|
t.Fatalf("expected Getting Started to be selected, got %q", model.currentDocName)
|
|
}
|
|
plain := ansi.Strip(model.viewer.View())
|
|
if !strings.Contains(plain, "first step") {
|
|
t.Fatalf("expected initial viewer content to render selected doc, got %q", plain)
|
|
}
|
|
}
|
|
|
|
func TestDocsModelSlashFocusesBrowserAndStartsFiltering(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 32)
|
|
model.focus = docsFocusViewer
|
|
|
|
updated, cmd := model.Update(tea.KeyPressMsg{Text: "/", Code: '/'})
|
|
if cmd == nil {
|
|
t.Fatal("expected slash to focus the search input")
|
|
}
|
|
|
|
updatedModel := updated.(*docsModel)
|
|
if updatedModel.focus != docsFocusBrowser {
|
|
t.Fatalf("expected slash to move focus to browser, got %v", updatedModel.focus)
|
|
}
|
|
if updatedModel.filterState != docsFilterStateFiltering {
|
|
t.Fatalf("expected slash to enter filtering state, got %v", updatedModel.filterState)
|
|
}
|
|
}
|
|
|
|
func TestDocsModelFilteringUpdatesVisibleEntries(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 32)
|
|
|
|
model.startFiltering()
|
|
model.browserFilter.SetValue("oper")
|
|
model.updateFilteredEntries(false)
|
|
|
|
visible := model.visibleEntries()
|
|
if len(visible) != 1 || visible[0].Name != "Operators" {
|
|
t.Fatalf("expected Operators to be the only filtered result, got %+v", visible)
|
|
}
|
|
if model.currentDocName != "Operators" {
|
|
t.Fatalf("expected filtering to sync selection to Operators, got %q", model.currentDocName)
|
|
}
|
|
}
|
|
|
|
func TestDocsModelEnterMovesFocusToViewer(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 32)
|
|
|
|
updated, _ := model.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
|
|
updatedModel := updated.(*docsModel)
|
|
if updatedModel.focus != docsFocusViewer {
|
|
t.Fatalf("expected enter to focus viewer, got %v", updatedModel.focus)
|
|
}
|
|
}
|
|
|
|
func TestDocsModelEscClearsAppliedFilter(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 32)
|
|
|
|
model.startFiltering()
|
|
model.browserFilter.SetValue("net")
|
|
model.finishFiltering(false)
|
|
|
|
updated, _ := model.Update(tea.KeyPressMsg{Code: tea.KeyEsc})
|
|
updatedModel := updated.(*docsModel)
|
|
if updatedModel.filterState != docsFilterStateUnfiltered {
|
|
t.Fatalf("expected esc to clear the filter, got %v", updatedModel.filterState)
|
|
}
|
|
if len(updatedModel.visibleEntries()) != len(sampleDocs()) {
|
|
t.Fatalf("expected filter clear to restore all docs, got %d visible", len(updatedModel.visibleEntries()))
|
|
}
|
|
}
|
|
|
|
func TestDocsModelBrowserBodyPinsHelpToBottom(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(120, 28)
|
|
|
|
body := ansi.Strip(model.renderBrowserBody(model.browserWidth, model.browserHeight))
|
|
lines := strings.Split(body, "\n")
|
|
if len(lines) != model.browserHeight {
|
|
t.Fatalf("expected browser body height %d, got %d lines", model.browserHeight, len(lines))
|
|
}
|
|
if !strings.Contains(lines[len(lines)-1], "q quit") {
|
|
t.Fatalf("expected help controls on the last browser line, got %q", lines[len(lines)-1])
|
|
}
|
|
}
|
|
|
|
func TestDocsWindowPollSchedulesResizeAndKeepsPolling(t *testing.T) {
|
|
model := newDocsModel(sampleDocs())
|
|
model.applyWindowSize(100, 28)
|
|
|
|
updated, cmd := model.Update(docsWindowPollMsg{width: 120, height: 34})
|
|
if cmd == nil {
|
|
t.Fatal("expected window poll to schedule resize handling")
|
|
}
|
|
|
|
updatedModel := updated.(*docsModel)
|
|
if updatedModel.width != 100 || updatedModel.height != 28 {
|
|
t.Fatalf("expected poll tick to leave dimensions unchanged until WindowSizeMsg, got %dx%d", updatedModel.width, updatedModel.height)
|
|
}
|
|
|
|
msg := cmd()
|
|
batch, ok := msg.(tea.BatchMsg)
|
|
if !ok {
|
|
t.Fatalf("expected poll tick to return tea.BatchMsg, got %T", msg)
|
|
}
|
|
if len(batch) != 2 {
|
|
t.Fatalf("expected poll tick batch size 2, got %d", len(batch))
|
|
}
|
|
|
|
var sawWindowSize bool
|
|
var sawPollTick bool
|
|
for _, subcmd := range batch {
|
|
if subcmd == nil {
|
|
continue
|
|
}
|
|
submsg := subcmd()
|
|
switch msg := submsg.(type) {
|
|
case tea.WindowSizeMsg:
|
|
sawWindowSize = true
|
|
if msg.Width != 120 || msg.Height != 34 {
|
|
t.Fatalf("expected resize 120x34, got %dx%d", msg.Width, msg.Height)
|
|
}
|
|
case docsWindowPollMsg:
|
|
sawPollTick = true
|
|
}
|
|
}
|
|
|
|
if !sawWindowSize {
|
|
t.Fatal("expected poll tick to emit WindowSizeMsg")
|
|
}
|
|
if !sawPollTick {
|
|
t.Fatal("expected poll tick to schedule another poll")
|
|
}
|
|
}
|