Skip to content

Commit 00f47e4

Browse files
Render Docs smart chips explicitly
1 parent a197727 commit 00f47e4

4 files changed

Lines changed: 329 additions & 5 deletions

File tree

docs/commands/gog-docs-cat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ gog docs (doc) cat (text,read) <docId> [flags]
2121
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
2222
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email, alias, or auto for authenticated Google API commands |
2323
| `--all-tabs` | `bool` | | Show all tabs with headers |
24+
| `--chips` | `bool` | | Render Google Docs smart chips inline in text output |
2425
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
2526
| `--color` | `string` | auto | Color output: auto\|always\|never |
2627
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |

internal/cmd/docs_commands_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,59 @@ func newTabsTestServer(t *testing.T) (*docs.Service, func()) {
353353
return docSvc, srv.Close
354354
}
355355

356+
func newSmartChipDocsTestServer(t *testing.T) (*docs.Service, func()) {
357+
t.Helper()
358+
359+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
360+
if !strings.HasPrefix(r.URL.Path, "/v1/documents/") || r.Method != http.MethodGet {
361+
http.NotFound(w, r)
362+
return
363+
}
364+
365+
w.Header().Set("Content-Type", "application/json")
366+
_ = json.NewEncoder(w).Encode(map[string]any{
367+
"documentId": "doc1",
368+
"title": "Smart chip doc",
369+
"body": map[string]any{"content": []any{map[string]any{
370+
"paragraph": map[string]any{"elements": []any{
371+
map[string]any{"startIndex": 1, "endIndex": 11, "textRun": map[string]any{"content": "Reviewer: "}},
372+
map[string]any{"startIndex": 11, "endIndex": 12, "person": map[string]any{
373+
"personProperties": map[string]any{"name": "Sample Person", "email": "[email protected]"},
374+
}},
375+
map[string]any{"startIndex": 12, "endIndex": 17, "textRun": map[string]any{"content": "\nDue: "}},
376+
map[string]any{"startIndex": 17, "endIndex": 18, "dateElement": map[string]any{
377+
"dateId": "date-1",
378+
"dateElementProperties": map[string]any{
379+
"displayText": "Jul 8, 2026",
380+
"timestamp": "1783468800",
381+
},
382+
}},
383+
map[string]any{"startIndex": 18, "endIndex": 24, "textRun": map[string]any{"content": "\nFile: "}},
384+
map[string]any{"startIndex": 24, "endIndex": 25, "richLink": map[string]any{
385+
"richLinkProperties": map[string]any{
386+
"title": "Plan Doc",
387+
"uri": "https://docs.google.com/document/d/plan",
388+
"mimeType": "application/vnd.google-apps.document",
389+
},
390+
}},
391+
map[string]any{"startIndex": 25, "endIndex": 26, "textRun": map[string]any{"content": "\n"}},
392+
}},
393+
}}},
394+
})
395+
}))
396+
397+
docSvc, err := docs.NewService(context.Background(),
398+
option.WithoutAuthentication(),
399+
option.WithHTTPClient(srv.Client()),
400+
option.WithEndpoint(srv.URL+"/"),
401+
)
402+
if err != nil {
403+
t.Fatalf("NewDocsService: %v", err)
404+
}
405+
406+
return docSvc, srv.Close
407+
}
408+
356409
func runDocsCatCommand(t *testing.T, svc *docs.Service, args []string, jsonMode bool) executeTestResult {
357410
t.Helper()
358411

@@ -629,6 +682,81 @@ func TestDocsCat_CaseInsensitiveTabTitle(t *testing.T) {
629682
}
630683
}
631684

685+
func TestDocsCat_SmartChipsAreOptInForTextOutput(t *testing.T) {
686+
t.Parallel()
687+
688+
docSvc, cleanup := newSmartChipDocsTestServer(t)
689+
defer cleanup()
690+
691+
result := runDocsCatCommand(t, docSvc, []string{"doc1"}, false)
692+
if result.err != nil {
693+
t.Fatalf("cat: %v", result.err)
694+
}
695+
if got, want := result.stdout, "Reviewer: \nDue: \nFile: \n"; got != want {
696+
t.Fatalf("default text changed\n got: %q\nwant: %q", got, want)
697+
}
698+
699+
result = runDocsCatCommand(t, docSvc, []string{"doc1", "--chips"}, false)
700+
if result.err != nil {
701+
t.Fatalf("cat --chips: %v", result.err)
702+
}
703+
want := "Reviewer: @Sample Person <[email protected]>\nDue: Jul 8, 2026\nFile: [Plan Doc](https://docs.google.com/document/d/plan)\n"
704+
if result.stdout != want {
705+
t.Fatalf("unexpected rendered chip text\n got: %q\nwant: %q", result.stdout, want)
706+
}
707+
}
708+
709+
func TestDocsCat_JSONAddsSmartChipDataWithoutChangingText(t *testing.T) {
710+
t.Parallel()
711+
712+
docSvc, cleanup := newSmartChipDocsTestServer(t)
713+
defer cleanup()
714+
715+
result := runDocsCatCommand(t, docSvc, []string{"doc1"}, true)
716+
if result.err != nil {
717+
t.Fatalf("cat --json: %v", result.err)
718+
}
719+
720+
var out struct {
721+
Text string `json:"text"`
722+
RenderedText string `json:"renderedText"`
723+
Chips []struct {
724+
Type string `json:"type"`
725+
Text string `json:"text"`
726+
StartIndex int64 `json:"startIndex"`
727+
EndIndex int64 `json:"endIndex"`
728+
Name string `json:"name"`
729+
Email string `json:"email"`
730+
Display string `json:"displayText"`
731+
Title string `json:"title"`
732+
URI string `json:"uri"`
733+
} `json:"chips"`
734+
}
735+
if err := json.Unmarshal([]byte(result.stdout), &out); err != nil {
736+
t.Fatalf("JSON parse: %v\nraw: %q", err, result.stdout)
737+
}
738+
if got, want := out.Text, "Reviewer: \nDue: \nFile: \n"; got != want {
739+
t.Fatalf("text changed\n got: %q\nwant: %q", got, want)
740+
}
741+
if !strings.Contains(out.RenderedText, "@Sample Person <[email protected]>") ||
742+
!strings.Contains(out.RenderedText, "Jul 8, 2026") ||
743+
!strings.Contains(out.RenderedText, "[Plan Doc](https://docs.google.com/document/d/plan)") {
744+
t.Fatalf("renderedText missing smart chip renderings: %q", out.RenderedText)
745+
}
746+
if len(out.Chips) != 3 {
747+
t.Fatalf("chips length = %d, want 3: %#v", len(out.Chips), out.Chips)
748+
}
749+
if out.Chips[0].Type != "person" || out.Chips[0].Name != "Sample Person" || out.Chips[0].Email != "[email protected]" {
750+
t.Fatalf("unexpected person chip: %#v", out.Chips[0])
751+
}
752+
if out.Chips[1].Type != "date" || out.Chips[1].Display != "Jul 8, 2026" {
753+
t.Fatalf("unexpected date chip: %#v", out.Chips[1])
754+
}
755+
if out.Chips[2].Type != "richLink" || out.Chips[2].Title != "Plan Doc" || out.Chips[2].URI != "https://docs.google.com/document/d/plan" {
756+
t.Fatalf("unexpected rich link chip: %#v", out.Chips[2])
757+
}
758+
}
759+
632760
func TestDocsCat_BackwardCompatibility(t *testing.T) {
633761
t.Parallel()
634762

0 commit comments

Comments
 (0)