github.com/olliephillips/hugo@v0.42.2/hugolib/embedded_shortcodes_test.go (about) 1 // Copyright 2016 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugolib 15 16 import ( 17 "encoding/json" 18 "fmt" 19 "html/template" 20 "strings" 21 "testing" 22 23 "path/filepath" 24 25 "github.com/gohugoio/hugo/deps" 26 27 "github.com/gohugoio/hugo/tpl" 28 "github.com/stretchr/testify/require" 29 ) 30 31 const ( 32 testBaseURL = "http://foo/bar" 33 ) 34 35 func TestShortcodeCrossrefs(t *testing.T) { 36 t.Parallel() 37 38 for _, relative := range []bool{true, false} { 39 doTestShortcodeCrossrefs(t, relative) 40 } 41 } 42 43 func doTestShortcodeCrossrefs(t *testing.T, relative bool) { 44 var ( 45 cfg, fs = newTestCfg() 46 ) 47 48 cfg.Set("baseURL", testBaseURL) 49 50 var refShortcode string 51 var expectedBase string 52 53 if relative { 54 refShortcode = "relref" 55 expectedBase = "/bar" 56 } else { 57 refShortcode = "ref" 58 expectedBase = testBaseURL 59 } 60 61 path := filepath.FromSlash("blog/post.md") 62 in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path) 63 64 writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in) 65 66 expected := fmt.Sprintf(`%s/simple/url/`, expectedBase) 67 68 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 69 70 require.Len(t, s.RegularPages, 1) 71 72 output := string(s.RegularPages[0].content()) 73 74 if !strings.Contains(output, expected) { 75 t.Errorf("Got\n%q\nExpected\n%q", output, expected) 76 } 77 } 78 79 func TestShortcodeHighlight(t *testing.T) { 80 t.Parallel() 81 82 for _, this := range []struct { 83 in, expected string 84 }{ 85 {`{{< highlight java >}} 86 void do(); 87 {{< /highlight >}}`, 88 `(?s)<div class="highlight"><pre style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java"`, 89 }, 90 {`{{< highlight java "style=friendly" >}} 91 void do(); 92 {{< /highlight >}}`, 93 `(?s)<div class="highlight"><pre style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java">`, 94 }, 95 } { 96 97 var ( 98 cfg, fs = newTestCfg() 99 th = testHelper{cfg, fs, t} 100 ) 101 102 cfg.Set("pygmentsStyle", "bw") 103 cfg.Set("pygmentsUseClasses", false) 104 105 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 106 title: Shorty 107 --- 108 %s`, this.in)) 109 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 110 111 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 112 113 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 114 115 } 116 } 117 118 func TestShortcodeFigure(t *testing.T) { 119 t.Parallel() 120 121 for _, this := range []struct { 122 in, expected string 123 }{ 124 { 125 `{{< figure src="/img/hugo-logo.png" >}}`, 126 "(?s)\n<figure>.*?<img src=\"/img/hugo-logo.png\" />.*?</figure>\n", 127 }, 128 { 129 // set alt 130 `{{< figure src="/img/hugo-logo.png" alt="Hugo logo" >}}`, 131 "(?s)\n<figure>.*?<img src=\"/img/hugo-logo.png\" alt=\"Hugo logo\" />.*?</figure>\n", 132 }, 133 // set title 134 { 135 `{{< figure src="/img/hugo-logo.png" title="Hugo logo" >}}`, 136 "(?s)\n<figure>.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<h4>Hugo logo</h4>.*?</figcaption>.*?</figure>\n", 137 }, 138 // set attr and attrlink 139 { 140 `{{< figure src="/img/hugo-logo.png" attr="Hugo logo" attrlink="/img/hugo-logo.png" >}}`, 141 "(?s)\n<figure>.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n", 142 }, 143 } { 144 145 var ( 146 cfg, fs = newTestCfg() 147 th = testHelper{cfg, fs, t} 148 ) 149 150 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 151 title: Shorty 152 --- 153 %s`, this.in)) 154 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 155 156 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 157 158 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 159 160 } 161 } 162 163 func TestShortcodeYoutube(t *testing.T) { 164 t.Parallel() 165 166 for _, this := range []struct { 167 in, expected string 168 }{ 169 { 170 `{{< youtube w7Ft2ymGmfc >}}`, 171 "(?s)\n<div style=\".*?\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" style=\".*?\" allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>\n", 172 }, 173 // set class 174 { 175 `{{< youtube w7Ft2ymGmfc video>}}`, 176 "(?s)\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\" allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>\n", 177 }, 178 // set class and autoplay (using named params) 179 { 180 `{{< youtube id="w7Ft2ymGmfc" class="video" autoplay="true" >}}`, 181 "(?s)\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>", 182 }, 183 } { 184 var ( 185 cfg, fs = newTestCfg() 186 th = testHelper{cfg, fs, t} 187 ) 188 189 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 190 title: Shorty 191 --- 192 %s`, this.in)) 193 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 194 195 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 196 197 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 198 } 199 200 } 201 202 func TestShortcodeVimeo(t *testing.T) { 203 t.Parallel() 204 205 for _, this := range []struct { 206 in, expected string 207 }{ 208 { 209 `{{< vimeo 146022717 >}}`, 210 "(?s)\n<div style=\".*?\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" style=\".*?\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n", 211 }, 212 // set class 213 { 214 `{{< vimeo 146022717 video >}}`, 215 "(?s)\n<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n", 216 }, 217 // set class (using named params) 218 { 219 `{{< vimeo id="146022717" class="video" >}}`, 220 "(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>", 221 }, 222 } { 223 var ( 224 cfg, fs = newTestCfg() 225 th = testHelper{cfg, fs, t} 226 ) 227 228 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 229 title: Shorty 230 --- 231 %s`, this.in)) 232 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 233 234 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 235 236 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 237 238 } 239 } 240 241 func TestShortcodeGist(t *testing.T) { 242 t.Parallel() 243 244 for _, this := range []struct { 245 in, expected string 246 }{ 247 { 248 `{{< gist spf13 7896402 >}}`, 249 "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\"></script>", 250 }, 251 { 252 `{{< gist spf13 7896402 "img.html" >}}`, 253 "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>", 254 }, 255 } { 256 var ( 257 cfg, fs = newTestCfg() 258 th = testHelper{cfg, fs, t} 259 ) 260 261 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 262 title: Shorty 263 --- 264 %s`, this.in)) 265 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 266 267 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 268 269 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 270 271 } 272 } 273 274 func TestShortcodeTweet(t *testing.T) { 275 t.Parallel() 276 277 for i, this := range []struct { 278 in, resp, expected string 279 }{ 280 { 281 `{{< tweet 666616452582129664 >}}`, 282 `{"url":"https:\/\/twitter.com\/spf13\/status\/666616452582129664","author_name":"Steve Francia","author_url":"https:\/\/twitter.com\/spf13","html":"\u003Cblockquote class=\"twitter-tweet\"\u003E\u003Cp lang=\"en\" dir=\"ltr\"\u003EHugo 0.15 will have 30%+ faster render times thanks to this commit \u003Ca href=\"https:\/\/t.co\/FfzhM8bNhT\"\u003Ehttps:\/\/t.co\/FfzhM8bNhT\u003C\/a\u003E \u003Ca href=\"https:\/\/twitter.com\/hashtag\/gohugo?src=hash\"\u003E#gohugo\u003C\/a\u003E \u003Ca href=\"https:\/\/twitter.com\/hashtag\/golang?src=hash\"\u003E#golang\u003C\/a\u003E \u003Ca href=\"https:\/\/t.co\/ITbMNU2BUf\"\u003Ehttps:\/\/t.co\/ITbMNU2BUf\u003C\/a\u003E\u003C\/p\u003E— Steve Francia (@spf13) \u003Ca href=\"https:\/\/twitter.com\/spf13\/status\/666616452582129664\"\u003ENovember 17, 2015\u003C\/a\u003E\u003C\/blockquote\u003E\n\u003Cscript async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"\u003E\u003C\/script\u003E","width":550,"height":null,"type":"rich","cache_age":"3153600000","provider_name":"Twitter","provider_url":"https:\/\/twitter.com","version":"1.0"}`, 283 `(?s)^<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Hugo 0.15 will have 30%. faster render times thanks to this commit <a href="https://t.co/FfzhM8bNhT">https://t.co/FfzhM8bNhT</a> <a href="https://twitter.com/hashtag/gohugo.src=hash">#gohugo</a> <a href="https://twitter.com/hashtag/golang.src=hash">#golang</a> <a href="https://t.co/ITbMNU2BUf">https://t.co/ITbMNU2BUf</a></p>— Steve Francia .@spf13. <a href="https://twitter.com/spf13/status/666616452582129664">November 17, 2015</a></blockquote>.*?<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>`, 284 }, 285 } { 286 // overload getJSON to return mock API response from Twitter 287 tweetFuncMap := template.FuncMap{ 288 "getJSON": func(urlParts ...string) interface{} { 289 var v interface{} 290 err := json.Unmarshal([]byte(this.resp), &v) 291 if err != nil { 292 t.Fatalf("[%d] unexpected error in json.Unmarshal: %s", i, err) 293 return err 294 } 295 return v 296 }, 297 } 298 299 var ( 300 cfg, fs = newTestCfg() 301 th = testHelper{cfg, fs, t} 302 ) 303 304 withTemplate := func(templ tpl.TemplateHandler) error { 305 templ.(tpl.TemplateTestMocker).SetFuncs(tweetFuncMap) 306 return nil 307 } 308 309 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 310 title: Shorty 311 --- 312 %s`, this.in)) 313 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`) 314 315 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{}) 316 317 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 318 319 } 320 } 321 322 func TestShortcodeInstagram(t *testing.T) { 323 t.Parallel() 324 325 for i, this := range []struct { 326 in, hidecaption, resp, expected string 327 }{ 328 { 329 `{{< instagram BMokmydjG-M >}}`, 330 `0`, 331 `{"provider_url": "https://www.instagram.com", "media_id": "1380514280986406796_25025320", "author_name": "instagram", "height": null, "thumbnail_url": "https://scontent-amt2-1.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/15048135_1880160212214218_7827880881132929024_n.jpg?ig_cache_key=MTM4MDUxNDI4MDk4NjQwNjc5Ng%3D%3D.2", "thumbnail_width": 640, "thumbnail_height": 640, "provider_name": "Instagram", "title": "Today, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories.\nBoomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode.\nYou can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they'll see a pop-up that takes them to that profile.\nYou may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app.\nTo learn more about today\u2019s updates, check out help.instagram.com.\nThese updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.", "html": "\u003cblockquote class=\"instagram-media\" data-instgrm-captioned data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\"\u003e\u003cdiv style=\"padding:8px;\"\u003e \u003cdiv style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;\"\u003e \u003cdiv style=\" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\"\u003e\u003c/div\u003e\u003c/div\u003e \u003cp style=\" margin:8px 0 0 0; padding:0 4px;\"\u003e \u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;\" target=\"_blank\"\u003eToday, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories. Boomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode. You can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they\u0026#39;ll see a pop-up that takes them to that profile. You may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app. To learn more about today\u2019s updates, check out help.instagram.com. These updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.\u003c/a\u003e\u003c/p\u003e \u003cp style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003eA photo posted by Instagram (@instagram) on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`, 332 `(?s)<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" .*defer src="//platform.instagram.com/en_US/embeds.js"></script>`, 333 }, 334 { 335 `{{< instagram BMokmydjG-M hidecaption >}}`, 336 `1`, 337 `{"provider_url": "https://www.instagram.com", "media_id": "1380514280986406796_25025320", "author_name": "instagram", "height": null, "thumbnail_url": "https://scontent-amt2-1.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/15048135_1880160212214218_7827880881132929024_n.jpg?ig_cache_key=MTM4MDUxNDI4MDk4NjQwNjc5Ng%3D%3D.2", "thumbnail_width": 640, "thumbnail_height": 640, "provider_name": "Instagram", "title": "Today, we\u2019re introducing a few new tools to help you make your story even more fun: Boomerang and mentions. We\u2019re also starting to test links inside some stories.\nBoomerang lets you turn everyday moments into something fun and unexpected. Now you can easily take a Boomerang right inside Instagram. Swipe right from your feed to open the stories camera. A new format picker under the record button lets you select \u201cBoomerang\u201d mode.\nYou can also now share who you\u2019re with or who you\u2019re thinking of by mentioning them in your story. When you add text to your story, type \u201c@\u201d followed by a username and select the person you\u2019d like to mention. Their username will appear underlined in your story. And when someone taps the mention, they'll see a pop-up that takes them to that profile.\nYou may begin to spot \u201cSee More\u201d links at the bottom of some stories. This is a test that lets verified accounts add links so it\u2019s easy to learn more. From your favorite chefs\u2019 recipes to articles from top journalists or concert dates from the musicians you love, tap \u201cSee More\u201d or swipe up to view the link right inside the app.\nTo learn more about today\u2019s updates, check out help.instagram.com.\nThese updates for Instagram Stories are available as part of Instagram version 9.7 available for iOS in the Apple App Store, for Android in Google Play and for Windows 10 in the Windows Store.", "html": "\u003cblockquote class=\"instagram-media\" data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\"\u003e\u003cdiv style=\"padding:8px;\"\u003e \u003cdiv style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;\"\u003e \u003cdiv style=\" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\"\u003e\u003c/div\u003e\u003c/div\u003e\u003cp style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003e\u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`, 338 `(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`, 339 }, 340 } { 341 // overload getJSON to return mock API response from Instagram 342 instagramFuncMap := template.FuncMap{ 343 "getJSON": func(urlParts ...string) interface{} { 344 var v interface{} 345 err := json.Unmarshal([]byte(this.resp), &v) 346 if err != nil { 347 t.Fatalf("[%d] unexpected error in json.Unmarshal: %s", i, err) 348 return err 349 } 350 return v 351 }, 352 } 353 354 var ( 355 cfg, fs = newTestCfg() 356 th = testHelper{cfg, fs, t} 357 ) 358 359 withTemplate := func(templ tpl.TemplateHandler) error { 360 templ.(tpl.TemplateTestMocker).SetFuncs(instagramFuncMap) 361 return nil 362 } 363 364 writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`--- 365 title: Shorty 366 --- 367 %s`, this.in)) 368 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content | safeHTML }}`) 369 370 buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{}) 371 372 th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected) 373 374 } 375 }