app/document_test.go

main
skunky-art/app/document_test.go history · blame · raw

122 lines · 5992 bytes

  1package app
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"github.com/krazywarez/devianter"
  8)
  9
 10// docText wraps a document body the way DeviantArt stores it.
 11func docText(body string) devianter.Text {
 12	var t devianter.Text
 13	t.Html.Markup = `{"version":1,"document":{"type":"doc","content":[` + body + `]},"features":[]}`
 14	return t
 15}
 16
 17func withDocConfig(t *testing.T) {
 18	t.Helper()
 19	proxy, uri, nsfw := CFG.Proxy, CFG.URI, CFG.Nsfw
 20	CFG.Proxy, CFG.URI, CFG.Nsfw = true, "/", true
 21	t.Cleanup(func() { CFG.Proxy, CFG.URI, CFG.Nsfw = proxy, uri, nsfw })
 22}
 23
 24// TestDocumentParagraphsMarksAndBreaks uses the shape seen on live
 25// descriptions: paragraphs of text with link, underline and italic marks.
 26func TestDocumentParagraphsMarksAndBreaks(t *testing.T) {
 27	withDocConfig(t)
 28	out := ParseDescription("http://localhost", docText(`
 29	{"type":"paragraph","attrs":{"textAlign":"center"},"content":[
 30	  {"type":"text","text":"| "},
 31	  {"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.deviantart.com/users/outgoing?https://www.patreon.com/x","target":"_blank"}},{"type":"underline"}],"text":"PATREON"},
 32	  {"type":"hardBreak"},
 33	  {"type":"text","marks":[{"type":"italic"}],"text":"Finished <YCH> for "},
 34	  {"type":"text","marks":[{"type":"textStyle"}],"text":"plain"}
 35	]}`))
 36	for _, want := range []string{
 37		`<p>| <a target="_blank" href="https://www.patreon.com/x"><u>PATREON</u></a><br><i>Finished &lt;YCH&gt; for </i>plain</p>`,
 38	} {
 39		if !strings.Contains(out, want) {
 40			t.Errorf("output lacks %q:\n%s", want, out)
 41		}
 42	}
 43}
 44
 45func TestDocumentBlocksAndLists(t *testing.T) {
 46	withDocConfig(t)
 47	out := ParseDescription("http://localhost", docText(`
 48	{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Title"}]},
 49	{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","marks":[{"type":"bold"}],"text":"one"}]}]}]},
 50	{"type":"orderedList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"two"}]}]}]},
 51	{"type":"blockquote","content":[{"type":"paragraph","content":[{"type":"text","text":"quoted"}]}]},
 52	{"type":"codeBlock","content":[{"type":"text","text":"x < y"}]},
 53	{"type":"horizontalRule"},
 54	{"type":"mystery","content":[{"type":"text","text":"still shown"}]},
 55	{"type":"mysteryLeaf","attrs":{"x":1}}`))
 56	for _, want := range []string{"<h2>Title</h2>", "<ul><li><p><b>one</b></p></li></ul>", "<ol><li><p>two</p></li></ol>", "<blockquote><p>quoted</p></blockquote>", "<pre>x &lt; y</pre>", "<hr>", "still shown"} {
 57		if !strings.Contains(out, want) {
 58			t.Errorf("output lacks %q:\n%s", want, out)
 59		}
 60	}
 61}
 62
 63// TestDocumentEmotes is the new-format half of #6: an official emote with a
 64// source image maps to the emote route by file name, one without a source
 65// falls back to its code.
 66func TestDocumentEmotes(t *testing.T) {
 67	withDocConfig(t)
 68	out := ParseDescription("http://localhost", docText(`
 69	{"type":"paragraph","content":[
 70	  {"type":"da-emote","attrs":{"data-emote":":star:","data-type":"official","src":"https://e.deviantart.net/emoticons/s/star_full.gif","width":17,"height":16,"url":null,"title":null}},
 71	  {"type":"da-emote","attrs":{"data-emote":":love:","data-type":"official","src":null,"width":null,"height":null,"url":null,"title":null}},
 72	  {"type":"da-emote","attrs":{"data-emote":"","data-type":"custom","src":null}}
 73	]}`))
 74	for _, want := range []string{`src="http://localhost/media/emojitar/star_full?type=e" alt=":star:"`, `src="http://localhost/media/emojitar/love?type=e" alt=":love:"`} {
 75		if !strings.Contains(out, want) {
 76			t.Errorf("output lacks %q:\n%s", want, out)
 77		}
 78	}
 79	if strings.Count(out, "<img") != 2 {
 80		t.Errorf("want 2 images (the nameless emote renders nothing):\n%s", out)
 81	}
 82}
 83
 84func TestDocumentEmbeddedDeviationAndGif(t *testing.T) {
 85	withDocConfig(t)
 86	out := ParseDescription("http://localhost", docText(`
 87	{"type":"paragraph","content":[
 88	  {"type":"da-deviation-thumb","attrs":{"cropping":"fill","deviation":{"deviationId":1276762071,"url":"https://www.deviantart.com/ashiori-chan/art/CLOSED-YCH-Portrait-auction-1276762071","title":"[CLOSED] YCH","author":{"username":"AShiori-chan"},"media":{"baseUri":"https://images-wixmp-abc.wixmp.com/f/u/x.png","prettyName":"x_by_y","token":["tok.en.sig"],"types":[{"t":"fullview","h":1920,"w":1280}]}}}},
 89	  {"type":"da-gif","attrs":{"url":"https://media4.giphy.com/media/x/giphy.mp4","width":480,"height":362}}
 90	]}`))
 91	for _, want := range []string{`<a href="http://localhost/post/ashiori-chan/CLOSED-YCH-Portrait-auction-1276762071">`, `src="http://localhost/media/file/abc/`, `alt="AShiori-chan - [CLOSED] YCH"`, `<a target="_blank" href="https://media4.giphy.com/media/x/giphy.mp4">[GIF]</a>`} {
 92		if !strings.Contains(out, want) {
 93			t.Errorf("output lacks %q:\n%s", want, out)
 94		}
 95	}
 96}
 97
 98func TestDocumentHidesEmbeddedNSFWWhenDisabled(t *testing.T) {
 99	withDocConfig(t)
100	CFG.Nsfw = false
101	out := ParseDescription("http://localhost", docText(`
102	{"type":"paragraph","content":[{"type":"da-deviation","attrs":{"deviation":{"deviationId":1,"isMature":true,"url":"https://www.deviantart.com/a/art/b-1","title":"secret","author":{"username":"a"}}}}]}`))
103	if strings.Contains(out, "secret") {
104		t.Errorf("mature embed shown with nsfw off:\n%s", out)
105	}
106}
107
108// TestDraftJSAndHTMLStillDispatch pins that the two older formats still reach
109// their own parsers.
110func TestDraftJSAndHTMLStillDispatch(t *testing.T) {
111	withDocConfig(t)
112	var draft devianter.Text
113	draft.Html.Markup = `{"blocks":[{"text":"draft text","type":"unstyled","inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}`
114	if out := ParseDescription("http://localhost", draft); !strings.Contains(out, "draft text") {
115		t.Errorf("Draft.js description not rendered:\n%s", out)
116	}
117	var legacy devianter.Text
118	legacy.Html.Markup = `plain <b>html</b>`
119	if out := ParseDescription("http://localhost", legacy); !strings.Contains(out, "<b>html</b>") {
120		t.Errorf("HTML description not rendered:\n%s", out)
121	}
122}