github.com/rabbouni145/gg@v0.47.1/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 type=\"application/javascript\" src=\"//gist.github.com/spf13/7896402.js\"></script>",
   250  		},
   251  		{
   252  			`{{< gist spf13 7896402 "img.html" >}}`,
   253  			"(?s)^<script type=\"application/javascript\" 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&mdash; 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>&mdash; 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  }