//go:build cgo // // Copyright 2026 The InfiniFlow Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package parser import ( "encoding/base64" "encoding/json" "strings" "testing" ) func TestBuildDOCXJSONSections_Paragraphs(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"paragraph","content":[{"type":"text","text":"Hello world"}],"style":"Normal"} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1", len(sections)) } item := sections[0] if got, ok := item["text"].(string); !ok || got != "Hello world" { t.Errorf("text = %q, want %q", got, "Hello world") } if got, ok := item["doc_type_kwd"].(string); !ok || got != "text" { t.Errorf("doc_type_kwd = %q, want %q", got, "text") } } func TestBuildDOCXJSONSections_Headings(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"heading","level":1,"content":[{"type":"text","text":"Chapter 1"}]} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1", len(sections)) } item := sections[0] if got, ok := item["text"].(string); !ok || got != "Chapter 1" { t.Errorf("text = %q, want %q", got, "Chapter 1") } if got, ok := item["doc_type_kwd"].(string); !ok || got != "text" { t.Errorf("doc_type_kwd = %q, want %q", got, "text") } if got, ok := item["ck_type"].(string); !ok || got != "heading" { t.Errorf("ck_type = %q, want %q", got, "heading") } } func TestBuildDOCXJSONSections_Images(t *testing.T) { b64 := base64.StdEncoding.EncodeToString([]byte("fake-image-data")) ir := `{"sections":[{"title":"","elements":[ {"type":"image","data":"` + b64 + `"} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1", len(sections)) } item := sections[0] if got, ok := item["text"].(string); !ok || got != "" { t.Errorf("text = %q, want empty", got) } if got, ok := item["image"].(string); !ok || got != b64 { t.Errorf("image = %q, want %q", got, b64) } if got, ok := item["doc_type_kwd"].(string); !ok || got != "image" { t.Errorf("doc_type_kwd = %q, want %q", got, "image") } } func TestBuildDOCXJSONSections_Tables(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"table","rows":[ {"cells":[{"content":[{"type":"paragraph","content":[{"type":"text","text":"A1"}]}]},{"content":[{"type":"paragraph","content":[{"type":"text","text":"B1"}]}]}]}, {"cells":[{"content":[{"type":"paragraph","content":[{"type":"text","text":"A2"}]}]},{"content":[{"type":"paragraph","content":[{"type":"text","text":"B2"}]}]}]} ]} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1", len(sections)) } item := sections[0] html, ok := item["text"].(string) if !ok { t.Fatal("text field missing or not string") } if !strings.Contains(html, "") || !strings.Contains(html, "
") { t.Errorf("html = %q, missing tags", html) } if !strings.Contains(html, "") || !strings.Contains(html, "") { t.Errorf("html = %q, missing tags", html) } if !strings.Contains(html, "") || !strings.Contains(html, "") { t.Errorf("html = %q, missing cell content", html) } if got, ok := item["doc_type_kwd"].(string); !ok || got != "table" { t.Errorf("doc_type_kwd = %q, want %q", got, "table") } } func TestBuildDOCXJSONSections_MixedContent(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"paragraph","content":[{"type":"text","text":"First para"}]}, {"type":"image","data":"aW1hZ2U="}, {"type":"table","rows":[{"cells":[{"content":[{"type":"paragraph","content":[{"type":"text","text":"cell"}]}]}]}]}, {"type":"heading","level":2,"content":[{"type":"text","text":"Sub title"}]} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 4 { t.Fatalf("got %d sections, want 4", len(sections)) } // paragraph first if got, _ := sections[0]["doc_type_kwd"].(string); got != "text" { t.Errorf("item[0].doc_type_kwd = %q, want %q", got, "text") } // image second if got, _ := sections[1]["doc_type_kwd"].(string); got != "image" { t.Errorf("item[1].doc_type_kwd = %q, want %q", got, "image") } // table third if got, _ := sections[2]["doc_type_kwd"].(string); got != "table" { t.Errorf("item[2].doc_type_kwd = %q, want %q", got, "table") } // heading fourth if got, _ := sections[3]["doc_type_kwd"].(string); got != "text" { t.Errorf("item[3].doc_type_kwd = %q, want %q", got, "text") } if got, _ := sections[3]["ck_type"].(string); got != "heading" { t.Errorf("item[3].ck_type = %q, want %q", got, "heading") } } func TestBuildDOCXJSONSections_List(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"list","ordered":false,"items":[ {"content":[{"type":"paragraph","content":[{"type":"text","text":"Item 1"}]}]}, {"content":[{"type":"paragraph","content":[{"type":"text","text":"Item 2"}]}]}, {"content":[{"type":"paragraph","content":[{"type":"text","text":"Item 3"}]}]} ],"level":0} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 3 { t.Fatalf("got %d sections, want 3 (each list item should be a section)", len(sections)) } for i, want := range []string{"Item 1", "Item 2", "Item 3"} { if got, _ := sections[i]["text"].(string); got != want { t.Errorf("section[%d].text = %q, want %q", i, got, want) } if got, ok := sections[i]["doc_type_kwd"].(string); !ok || got != "text" { t.Errorf("section[%d].doc_type_kwd = %q, want %q", i, got, "text") } } } func TestBuildDOCXJSONSections_TextBox(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"text_box","content":[{"type":"paragraph","content":[{"type":"text","text":"Boxed paragraph"}]}],"width_emu":null} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1 (text_box content should become a section)", len(sections)) } if got, _ := sections[0]["text"].(string); got != "Boxed paragraph" { t.Errorf("text = %q, want %q", got, "Boxed paragraph") } if got, ok := sections[0]["doc_type_kwd"].(string); !ok || got != "text" { t.Errorf("doc_type_kwd = %q, want %q", got, "text") } } func TestBuildDOCXJSONSections_MixedWithList(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"paragraph","content":[{"type":"text","text":"Preamble"}],"style":"Normal"}, {"type":"list","ordered":false,"items":[ {"content":[{"type":"paragraph","content":[{"type":"text","text":"Bullet A"}]}]}, {"content":[{"type":"paragraph","content":[{"type":"text","text":"Bullet B"}]}]} ],"level":0}, {"type":"paragraph","content":[{"type":"text","text":"Trailer"}],"style":"Normal"} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 4 { t.Fatalf("got %d sections, want 4 (para + 2 list items + para)", len(sections)) } wants := []string{"Preamble", "Bullet A", "Bullet B", "Trailer"} for i, want := range wants { if got, _ := sections[i]["text"].(string); got != want { t.Errorf("section[%d].text = %q, want %q", i, got, want) } } } func TestBuildDOCXJSONSections_EmptySkipped(t *testing.T) { ir := `{"sections":[{"title":"","elements":[ {"type":"paragraph","content":[{"type":"text","text":""}]}, {"type":"paragraph","content":[{"type":"text","text":"Only this matters"}]}, {"type":"table","rows":[]} ]}]}` sections := buildDOCXJSONSections(ir) if len(sections) != 1 { t.Fatalf("got %d sections, want 1 (only non-empty paragraph)", len(sections)) } if got, _ := sections[0]["text"].(string); got != "Only this matters" { t.Errorf("text = %q, want %q", got, "Only this matters") } } func TestDocxIRTableToHTML_Empty(t *testing.T) { el := docxIRElement{Type: "table", Rows: []docxIRRow{}} html := docxIRTableToHTML(el) if html != "
A1B2
" { t.Errorf("empty table html = %q, want %q", html, "
") } } func TestDocxIRTableToHTML_Single(t *testing.T) { el := docxIRElement{ Type: "table", Rows: []docxIRRow{{ Cells: []docxIRCell{{ Content: []docxIRElement{{ Type: "paragraph", Content: json.RawMessage(`[{"type":"text","text":"hello"}]`), }}, }}, }}, } html := docxIRTableToHTML(el) want := "
hello
" if html != want { t.Errorf("single cell html = %q, want %q", html, want) } } func TestDocxIRTableToHTML_MultiRowCol(t *testing.T) { cell := func(text string) docxIRCell { return docxIRCell{ Content: []docxIRElement{{ Type: "paragraph", Content: rawJSON([]docxIRRun{{Type: "text", Text: text}}), }}, } } el := docxIRElement{ Type: "table", Rows: []docxIRRow{ {Cells: []docxIRCell{cell("A1"), cell("B1")}}, {Cells: []docxIRCell{cell("A2"), cell("B2")}}, }, } html := docxIRTableToHTML(el) want := "
A1B1
A2B2
" if html != want { t.Errorf("multi cell html = %q, want %q", html, want) } }