Files
larksuite-cli/shortcuts/mail/draft/htmltext_test.go
xzcong0820 cf35d1e499 feat(mail): auto-attach default signature on send/reply/forward (#1415)
* feat(mail): auto-attach default signature on send/reply/forward

- Add exported PlainTextFromHTML wrapper in draft/htmltext.go
- Add DefaultSendID/DefaultReplyID in signature/provider.go
- Add noSignatureFlag, autoResolveSignatureID, validateNoSignatureConflict,
  injectPlainTextSignature in signature_compose.go; remove validateSignatureWithPlainText
- mail_send, mail_draft_create: add --no-signature flag, auto-resolve default
  signature when no --signature-id given, inject plain-text sig in plain-text branch
- mail_reply, mail_reply_all, mail_forward: same flag/validate changes + timing fix
  (resolveSignature moved to after senderEmail is finalized)
- Update 5 reference docs: add --no-signature row, update --plain-text and
  --signature-id descriptions

---------

Co-authored-by: xzcong0820 <278082089+xzcong0820@users.noreply.github.com>
2026-06-15 20:04:05 +08:00

112 lines
2.6 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package draft
import (
"strings"
"testing"
xhtml "golang.org/x/net/html"
)
func TestPlainTextFromHTML(t *testing.T) {
tests := []struct {
name string
html string
want string
}{
{
name: "strips inline style tag",
html: `<html><head><style>body{color:red}</style></head><body><p>Hello</p></body></html>`,
want: "Hello",
},
{
name: "strips script tag",
html: `<html><body><p>Before</p><script>alert("xss")</script><p>After</p></body></html>`,
want: "Before\nAfter",
},
{
name: "strips noscript tag",
html: `<html><body><p>Visible</p><noscript>Fallback text</noscript></body></html>`,
want: "Visible",
},
{
name: "strips head and title",
html: `<html><head><title>Page Title</title></head><body>Content</body></html>`,
want: "Content",
},
{
name: "plain text passthrough",
html: `<div>Line one</div><div>Line two</div>`,
want: "Line one\nLine two",
},
{
name: "mixed non-text and text tags",
html: `<html><head><style>.a{}</style><link rel="stylesheet"/><meta charset="utf-8"/></head><body><div>Only this</div></body></html>`,
want: "Only this",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := plainTextFromHTML(tt.html)
if got != tt.want {
t.Errorf("plainTextFromHTML() = %q, want %q", got, tt.want)
}
})
}
}
func TestPlainTextFromHTMLDeepNesting(t *testing.T) {
// Build HTML with 10000 levels of nesting — would overflow the stack
// with the old recursive implementation.
const depth = 10_000
var b strings.Builder
for i := 0; i < depth; i++ {
b.WriteString("<div>")
}
b.WriteString("deep")
for i := 0; i < depth; i++ {
b.WriteString("</div>")
}
got := plainTextFromHTML(b.String())
if got != "deep" {
t.Errorf("deep nesting: got %q, want %q", got, "deep")
}
}
func TestIsHTMLNonTextTag(t *testing.T) {
tests := []struct {
tag string
want bool
}{
{"script", true},
{"style", true},
{"head", true},
{"meta", true},
{"noscript", true},
{"link", true},
{"title", true},
{"div", false},
{"p", false},
{"span", false},
}
for _, tt := range tests {
t.Run(tt.tag, func(t *testing.T) {
n := &xhtml.Node{Type: xhtml.ElementNode, Data: tt.tag}
if got := isHTMLNonTextTag(n); got != tt.want {
t.Errorf("isHTMLNonTextTag(%q) = %v, want %v", tt.tag, got, tt.want)
}
})
}
}
func TestPlainTextFromHTMLExported(t *testing.T) {
got := PlainTextFromHTML("<p>Hello world</p>")
if !strings.Contains(got, "Hello world") {
t.Fatalf("PlainTextFromHTML: expected to contain \"Hello world\", got %q", got)
}
}