// list parses <ul> and <ol> lists. // It returns nil if the list has no items. func list(ds *docState) types.Node { typ := nodeAttr(ds.cur, "type") if ds.cur.DataAtom == atom.Ol && typ == "" { typ = "1" } start, _ := strconv.Atoi(nodeAttr(ds.cur, "start")) list := types.NewItemsListNode(typ, start) for hn := findAtom(ds.cur, atom.Li); hn != nil; hn = hn.NextSibling { if hn.DataAtom != atom.Li { continue } ds.push(hn, ds.flags) nn := parseSubtree(ds) nn = compactNodes(nn) ds.pop() if len(nn) > 0 { list.NewItem(nn...) } } if len(list.Items) == 0 { return nil } if ds.lastNode != nil { switch ds.lastNode.Type() { case types.NodeHeaderCheck: list.MutateType(types.NodeItemsCheck) case types.NodeHeaderFAQ: list.MutateType(types.NodeItemsFAQ) } } return list }
func TestParseDoc(t *testing.T) { const markup = ` <html><head><style> .meta { color: #b7b7b7 } .code { font-family: "Courier New" } .term { font-family: "Consolas" } .btn { background-color: #6aa84f } .bold { font-weight: bold } .ita { font-style: italic } .nibox { background-color: #fce5cd } .survey { background-color: #cfe2f3 } </style></head> <body> <p class="title"><a name="a1"></a><span>Test Codelab</span></p> <h1><a name="a2"></a><span>Overview</span></h1> <p><span class="meta">Duration: 1:00</span></p> <img src="https://host/image.png"> <p><img src="https://host/small.png" style="height: 10px; width: 25.5px"> icon.</p> <h3><a name="a3"></a><span>What you’ll learn</span></h3> <ul class="start"> <li><span>First </span><span>One</span><sup><a href="#cmnt1" name="cmnt_ref1" target="_blank">[a]</a></sup></li> <li><span>Two </span><span><a href="https://google.com/url?q=http%3A%2F%2Fexample.com">Link</a></span></li> </ul> <ul><li><span>Three</span></li></ul> <p>This is<span class="code"> code</span>.</p> <p>Just <span>a</span> paragraph.</p> <p><a href="url">one</a><a href="url"> url</a></p> <p><span class="btn"><a href="http://example.com">Download Zip</a></span></p> <p> <span class="bold">Bo</span><span> </span><span class="bold">ld</span> <span class="ita"> italic</span> text <span class="bold ita">or both.</span></p> <h3><a href="http://host/file.java">a file</a></h3> <table cellpadding="0" cellspacing="0"><tbody><tr> <td colspan="1" rowspan="1"> <p><span class="code">start func() {<br>}</span></p> <p><span class="code"></span></p> <p><span class="code">func2() {<br>}</span><span class="code"> // comment</span></p> </td> </tr></tbody></table> <table cellpadding="0" cellspacing="0"><tbody><tr> <td colspan="1" rowspan="1"> <p><span class="term">adb shell am start -a VIEW \</span></p> <p><span class="term">-d "http://host" app</span></p> </td> </tr></tbody></table> <table cellpadding="0" cellspacing="0"><tbody><tr> <td class="nibox" colspan="1" rowspan="1"> <p><span class="bold">warning</span></p> <p><span>negative box.</span></p> </td> </tr></tbody></table> <table cellpadding="0" cellspacing="0"><tbody><tr> <td class="survey" colspan="1" rowspan="1"> <h4><a name="x"></a><span class="code">How</span><span class="ita"> will you use it?</span></h4> <ul><li class="bold"><span class="c5">Read it</span></li></ul> <ul><li class="c23 c47"><span class="c5">Read and complete</span></li></ul> <p class="c23 c44"><span class="c5"></span></p> <h4><a name="asd"></a><span>How</span><span> would you rate?</span></h4> <ul> <li class="c19 c47"><span class="c5">Novice</span></li> <li class="c19 c47"><span class="c5">Intermediate</span></li> <li class="c19 c47"><span class="c5">Proficient</span></li> </ul> <p class="c23 c44"><span class="c5"></span></p> </td> </tr></tbody></table> <div><p><a href="#cmnt_ref1" name="cmnt1">[a]</a><span class="c16 c8">Test comment.</span></p></div> </body> </html> ` c, err := Parse(markupReader(markup)) if err != nil { t.Fatal(err) } if c.Meta.Title != "Test Codelab" { t.Errorf("c.Meta.Title = %q; want Test Codelab", c.Meta.Title) } if c.Meta.ID != "test-codelab" { t.Errorf("c.ID = %q; want test-codelab", c.Meta.ID) } if len(c.Steps) == 0 { t.Fatalf("len(c.Steps) = 0") } step := c.Steps[0] if step.Title != "Overview" { t.Errorf("step.Title = %q; want Overview", step.Title) } content := types.NewListNode() img := types.NewImageNode("https://host/image.png") para := types.NewListNode(img) para.MutateBlock(true) content.Append(para) img = types.NewImageNode("https://host/small.png") img.MaxWidth = 25.5 para = types.NewListNode(img, types.NewTextNode(" icon.")) para.MutateBlock(true) content.Append(para) h := types.NewHeaderNode(3, types.NewTextNode("What you'll learn")) h.MutateType(types.NodeHeaderCheck) content.Append(h) list := types.NewItemsListNode("", 0) list.MutateType(types.NodeItemsCheck) list.NewItem().Append(types.NewTextNode("First One")) item := list.NewItem() item.Append(types.NewTextNode("Two ")) item.Append(types.NewURLNode("http://example.com", types.NewTextNode("Link"))) list.NewItem().Append(types.NewTextNode("Three")) content.Append(list) para = types.NewListNode() para.MutateBlock(true) para.Append(types.NewTextNode("This is ")) txt := types.NewTextNode("code") txt.Code = true para.Append(txt) para.Append(types.NewTextNode(".")) content.Append(para) para = types.NewListNode() para.MutateBlock(true) para.Append(types.NewTextNode("Just a paragraph.")) content.Append(para) u := types.NewURLNode("url", types.NewTextNode("one url")) para = types.NewListNode(u) para.MutateBlock(true) content.Append(para) btn := types.NewButtonNode(true, true, true, types.NewTextNode("Download Zip")) dl := types.NewURLNode("http://example.com", btn) para = types.NewListNode(dl) para.MutateBlock(true) content.Append(para) b := types.NewTextNode("Bo ld") b.Bold = true i := types.NewTextNode(" italic") i.Italic = true bi := types.NewTextNode("or both.") bi.Bold = true bi.Italic = true para = types.NewListNode(b, i, types.NewTextNode(" text "), bi) para.MutateBlock(true) content.Append(para) h = types.NewHeaderNode(3, types.NewURLNode( "http://host/file.java", types.NewTextNode("a file"))) content.Append(h) code := "start func() {\n}\n\nfunc2() {\n} // comment" cn := types.NewCodeNode(code, false) cn.MutateBlock(1) content.Append(cn) term := "adb shell am start -a VIEW \\\n-d \"http://host\" app" tn := types.NewCodeNode(term, true) tn.MutateBlock(2) content.Append(tn) b = types.NewTextNode("warning") b.Bold = true n1 := types.NewListNode(b) n1.MutateBlock(true) n2 := types.NewListNode(types.NewTextNode("negative box.")) n2.MutateBlock(true) box := types.NewInfoboxNode(types.InfoboxNegative, n1, n2) content.Append(box) sv := types.NewSurveyNode("test-codelab-1") sv.Groups = append(sv.Groups, &types.SurveyGroup{ Name: "How will you use it?", Options: []string{"Read it", "Read and complete"}, }) sv.Groups = append(sv.Groups, &types.SurveyGroup{ Name: "How would you rate?", Options: []string{"Novice", "Intermediate", "Proficient"}, }) content.Append(sv) html1, _ := render.HTML("", step.Content) html2, _ := render.HTML("", content) if html1 != html2 { t.Errorf("step.Content:\n\n%s\nwant:\n\n%s", html1, html2) } }