1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package export
import (
"os"
"os/exec"
"runtime"
"strings"
"testing"
)
func runCmd(name string, args ...string) (string, error) {
out, err := exec.Command(name, args...).CombinedOutput()
return string(out), err
}
func TestJSQuoteEscapesSpecialChars(t *testing.T) {
got := jsQuote(`/tmp/a 'quoted' \path/x.rtf`)
want := `'/tmp/a \'quoted\' \\path/x.rtf'`
if got != want {
t.Errorf("jsQuote = %q, want %q", got, want)
}
}
func TestClipboardScriptSetsBothFlavorsFromBothPaths(t *testing.T) {
script := clipboardScript("/tmp/clip.rtf", "/tmp/clip.txt")
for _, want := range []string{`'/tmp/clip.rtf'`, `'/tmp/clip.txt'`, "public.rtf", "public.utf8-plain-text", "setDataForType"} {
if !strings.Contains(script, want) {
t.Errorf("clipboardScript missing %q in:\n%s", want, script)
}
}
}
func TestHTMLToRTFProducesRTFHeader(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("textutil is macOS-only")
}
rtf, err := htmlToRTF(`<html><body><h1>Title</h1><p>Hello <b>world</b></p></body></html>`)
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(string(rtf), `{\rtf1`) {
t.Errorf("htmlToRTF output doesn't look like RTF: %q", firstLines(string(rtf), 1))
}
if !strings.Contains(string(rtf), "Title") || !strings.Contains(string(rtf), "world") {
t.Errorf("htmlToRTF output missing source text: %s", firstLines(string(rtf), 5))
}
}
// TestSetClipboardRichTextWritesBothFlavors actually overwrites the system
// clipboard, so it's opt-in only (GLINT_TEST_CLIPBOARD=1) rather than run by
// default under `go test ./...`.
func TestSetClipboardRichTextWritesBothFlavors(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("clipboard RTF flavor is macOS-only")
}
if os.Getenv("GLINT_TEST_CLIPBOARD") == "" {
t.Skip("set GLINT_TEST_CLIPBOARD=1 to run — this overwrites the system clipboard")
}
rtf, err := htmlToRTF(`<html><body><p>Hello <b>rich</b> world</p></body></html>`)
if err != nil {
t.Fatal(err)
}
if err := setClipboardRichText(rtf, "Hello rich world"); err != nil {
t.Fatal(err)
}
out, err := runCmd("osascript", "-e", "clipboard info")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, "RTF") {
t.Errorf("clipboard info missing RTF flavor: %s", out)
}
}
func TestCopyRichTextRoundTripsMarkdownToClipboard(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("rich text copy is macOS-only")
}
if os.Getenv("GLINT_TEST_CLIPBOARD") == "" {
t.Skip("set GLINT_TEST_CLIPBOARD=1 to run — this overwrites the system clipboard")
}
if err := CopyRichText("# Title\n\nHello **world**."); err != nil {
t.Fatal(err)
}
out, err := runCmd("osascript", "-e", "clipboard info")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, "RTF") {
t.Errorf("clipboard info missing RTF flavor after CopyRichText: %s", out)
}
}
// TestCopyRichTextHasNoPageBackground guards the bug found in manual
// verification: the full house-style Document() (used by Ctrl+E/-e) sets an
// explicit CSS background-color for print/screen, which textutil carries
// into RTF as a page background *and* a highlight color behind every run of
// text — visible in Cocoa apps as an unwanted highlight box around the
// pasted content. Rich-text copy renders a bare semantic fragment instead
// (no doc.css, no theme, no fonts), so pasted text inherits the target
// app's own background.
func TestCopyRichTextHasNoPageBackground(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("textutil is macOS-only")
}
html, err := richTextHTML("Hello **bold** world")
if err != nil {
t.Fatal(err)
}
rtf, err := htmlToRTF(html)
if err != nil {
t.Fatal(err)
}
for _, unwanted := range []string{`\background`, `\cb1`, `\cb2`, `\cb3`, `\highlight`} {
if strings.Contains(string(rtf), unwanted) {
t.Errorf("richTextHTML output carries a page/highlight background (%s) into RTF:\n%s", unwanted, rtf)
}
}
}
|