github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/hugolib/embedded_shortcodes_test.go (about)

     1  // Copyright 2019 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  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/spf13/cast"
    25  
    26  	"github.com/gohugoio/hugo/deps"
    27  
    28  	qt "github.com/frankban/quicktest"
    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  		c       = qt.New(t)
    47  	)
    48  
    49  	cfg.Set("baseURL", testBaseURL)
    50  
    51  	var refShortcode string
    52  	var expectedBase string
    53  
    54  	if relative {
    55  		refShortcode = "relref"
    56  		expectedBase = "/bar"
    57  	} else {
    58  		refShortcode = "ref"
    59  		expectedBase = testBaseURL
    60  	}
    61  
    62  	path := filepath.FromSlash("blog/post.md")
    63  	in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path)
    64  
    65  	writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in)
    66  
    67  	expected := fmt.Sprintf(`%s/simple/url/`, expectedBase)
    68  
    69  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
    70  
    71  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
    72  
    73  	content, err := s.RegularPages()[0].Content()
    74  	c.Assert(err, qt.IsNil)
    75  	output := cast.ToString(content)
    76  
    77  	if !strings.Contains(output, expected) {
    78  		t.Errorf("Got\n%q\nExpected\n%q", output, expected)
    79  	}
    80  }
    81  
    82  func TestShortcodeHighlight(t *testing.T) {
    83  	t.Parallel()
    84  
    85  	for _, this := range []struct {
    86  		in, expected string
    87  	}{
    88  		{
    89  			`{{< highlight java >}}
    90  void do();
    91  {{< /highlight >}}`,
    92  			`(?s)<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java"`,
    93  		},
    94  		{
    95  			`{{< highlight java "style=friendly" >}}
    96  void do();
    97  {{< /highlight >}}`,
    98  			`(?s)<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java">`,
    99  		},
   100  	} {
   101  
   102  		var (
   103  			cfg, fs = newTestCfg()
   104  			th      = newTestHelper(cfg, fs, t)
   105  		)
   106  
   107  		cfg.Set("markup.highlight.style", "bw")
   108  		cfg.Set("markup.highlight.noClasses", true)
   109  
   110  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   111  title: Shorty
   112  ---
   113  %s`, this.in))
   114  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   115  
   116  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   117  
   118  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   119  
   120  	}
   121  }
   122  
   123  func TestShortcodeFigure(t *testing.T) {
   124  	t.Parallel()
   125  
   126  	for _, this := range []struct {
   127  		in, expected string
   128  	}{
   129  		{
   130  			`{{< figure src="/img/hugo-logo.png" >}}`,
   131  			"(?s)<figure>.*?<img src=\"/img/hugo-logo.png\"/>.*?</figure>",
   132  		},
   133  		{
   134  			// set alt
   135  			`{{< figure src="/img/hugo-logo.png" alt="Hugo logo" >}}`,
   136  			"(?s)<figure>.*?<img src=\"/img/hugo-logo.png\".+?alt=\"Hugo logo\"/>.*?</figure>",
   137  		},
   138  		// set title
   139  		{
   140  			`{{< figure src="/img/hugo-logo.png" title="Hugo logo" >}}`,
   141  			"(?s)<figure>.*?<img src=\"/img/hugo-logo.png\"/>.*?<figcaption>.*?<h4>Hugo logo</h4>.*?</figcaption>.*?</figure>",
   142  		},
   143  		// set attr and attrlink
   144  		{
   145  			`{{< figure src="/img/hugo-logo.png" attr="Hugo logo" attrlink="/img/hugo-logo.png" >}}`,
   146  			"(?s)<figure>.*?<img src=\"/img/hugo-logo.png\"/>.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>",
   147  		},
   148  	} {
   149  
   150  		var (
   151  			cfg, fs = newTestCfg()
   152  			th      = newTestHelper(cfg, fs, t)
   153  		)
   154  
   155  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   156  title: Shorty
   157  ---
   158  %s`, this.in))
   159  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   160  
   161  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   162  
   163  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   164  
   165  	}
   166  }
   167  
   168  func TestShortcodeYoutube(t *testing.T) {
   169  	t.Parallel()
   170  
   171  	for _, this := range []struct {
   172  		in, expected string
   173  	}{
   174  		{
   175  			`{{< youtube w7Ft2ymGmfc >}}`,
   176  			"(?s)\n<div style=\".*?\">.*?<iframe src=\"https://www.youtube.com/embed/w7Ft2ymGmfc\" style=\".*?\" allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>\n",
   177  		},
   178  		// set class
   179  		{
   180  			`{{< youtube w7Ft2ymGmfc video>}}`,
   181  			"(?s)\n<div class=\"video\">.*?<iframe src=\"https://www.youtube.com/embed/w7Ft2ymGmfc\" allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>\n",
   182  		},
   183  		// set class and autoplay (using named params)
   184  		{
   185  			`{{< youtube id="w7Ft2ymGmfc" class="video" autoplay="true" >}}`,
   186  			"(?s)\n<div class=\"video\">.*?<iframe src=\"https://www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen title=\"YouTube Video\">.*?</iframe>.*?</div>",
   187  		},
   188  		// set custom title for accessibility)
   189  		{
   190  			`{{< youtube id="w7Ft2ymGmfc" title="A New Hugo Site in Under Two Minutes" >}}`,
   191  			"(?s)\n<div style=\".*?\">.*?<iframe src=\"https://www.youtube.com/embed/w7Ft2ymGmfc\" style=\".*?\" allowfullscreen title=\"A New Hugo Site in Under Two Minutes\">.*?</iframe>.*?</div>",
   192  		},
   193  	} {
   194  		var (
   195  			cfg, fs = newTestCfg()
   196  			th      = newTestHelper(cfg, fs, t)
   197  		)
   198  
   199  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   200  title: Shorty
   201  ---
   202  %s`, this.in))
   203  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   204  
   205  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   206  
   207  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   208  	}
   209  }
   210  
   211  func TestShortcodeVimeo(t *testing.T) {
   212  	t.Parallel()
   213  
   214  	for _, this := range []struct {
   215  		in, expected string
   216  	}{
   217  		{
   218  			`{{< vimeo 146022717 >}}`,
   219  			"(?s)\n<div style=\".*?\">.*?<iframe src=\"https://player.vimeo.com/video/146022717\" style=\".*?\" title=\"vimeo video\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n",
   220  		},
   221  		// set class
   222  		{
   223  			`{{< vimeo 146022717 video >}}`,
   224  			"(?s)\n<div class=\"video\">.*?<iframe src=\"https://player.vimeo.com/video/146022717\" title=\"vimeo video\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n",
   225  		},
   226  		// set vimeo title
   227  		{
   228  			`{{< vimeo 146022717 video my-title >}}`,
   229  			"(?s)\n<div class=\"video\">.*?<iframe src=\"https://player.vimeo.com/video/146022717\" title=\"my-title\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>\n",
   230  		},
   231  		// set class (using named params)
   232  		{
   233  			`{{< vimeo id="146022717" class="video" >}}`,
   234  			"(?s)^<div class=\"video\">.*?<iframe src=\"https://player.vimeo.com/video/146022717\" title=\"vimeo video\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>",
   235  		},
   236  		// set vimeo title (using named params)
   237  		{
   238  			`{{< vimeo id="146022717" class="video" title="my vimeo video" >}}`,
   239  			"(?s)^<div class=\"video\">.*?<iframe src=\"https://player.vimeo.com/video/146022717\" title=\"my vimeo video\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>",
   240  		},
   241  	} {
   242  		var (
   243  			cfg, fs = newTestCfg()
   244  			th      = newTestHelper(cfg, fs, t)
   245  		)
   246  
   247  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   248  title: Shorty
   249  ---
   250  %s`, this.in))
   251  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   252  
   253  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   254  
   255  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   256  
   257  	}
   258  }
   259  
   260  func TestShortcodeGist(t *testing.T) {
   261  	t.Parallel()
   262  
   263  	for _, this := range []struct {
   264  		in, expected string
   265  	}{
   266  		{
   267  			`{{< gist spf13 7896402 >}}`,
   268  			"(?s)^<script type=\"application/javascript\" src=\"https://gist.github.com/spf13/7896402.js\"></script>",
   269  		},
   270  		{
   271  			`{{< gist spf13 7896402 "img.html" >}}`,
   272  			"(?s)^<script type=\"application/javascript\" src=\"https://gist.github.com/spf13/7896402.js\\?file=img.html\"></script>",
   273  		},
   274  	} {
   275  		var (
   276  			cfg, fs = newTestCfg()
   277  			th      = newTestHelper(cfg, fs, t)
   278  		)
   279  
   280  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   281  title: Shorty
   282  ---
   283  %s`, this.in))
   284  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   285  
   286  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   287  
   288  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   289  
   290  	}
   291  }
   292  
   293  func TestShortcodeTweet(t *testing.T) {
   294  	t.Parallel()
   295  
   296  	for i, this := range []struct {
   297  		privacy            map[string]interface{}
   298  		in, resp, expected string
   299  	}{
   300  		{
   301  			map[string]interface{}{
   302  				"twitter": map[string]interface{}{
   303  					"simple": true,
   304  				},
   305  			},
   306  			`{{< tweet 666616452582129664 >}}`,
   307  			`{"author_name":"Steve Francia","author_url":"https://twitter.com/spf13","cache_age":"3153600000","height":null,"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\u0026amp;ref_src=twsrc%5Etfw\"\u003e#gohugo\u003c/a\u003e \u003ca href=\"https://twitter.com/hashtag/golang?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#golang\u003c/a\u003e \u003ca href=\"https://t.co/ITbMNU2BUf\"\u003ehttps://t.co/ITbMNU2BUf\u003c/a\u003e\u003c/p\u003e\u0026mdash; Steve Francia (@spf13) \u003ca href=\"https://twitter.com/spf13/status/666616452582129664?ref_src=twsrc%5Etfw\"\u003eNovember 17, 2015\u003c/a\u003e\u003c/blockquote\u003e\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n","provider_name":"Twitter","provider_url":"https://twitter.com","type":"rich","url":"https://twitter.com/spf13/status/666616452582129664","version":"1.0","width":550}`,
   308  			`.twitter-tweet a`,
   309  		},
   310  		{
   311  			map[string]interface{}{
   312  				"twitter": map[string]interface{}{
   313  					"simple": false,
   314  				},
   315  			},
   316  			`{{< tweet 666616452582129664 >}}`,
   317  			`{"author_name":"Steve Francia","author_url":"https://twitter.com/spf13","cache_age":"3153600000","height":null,"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\u0026amp;ref_src=twsrc%5Etfw\"\u003e#gohugo\u003c/a\u003e \u003ca href=\"https://twitter.com/hashtag/golang?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#golang\u003c/a\u003e \u003ca href=\"https://t.co/ITbMNU2BUf\"\u003ehttps://t.co/ITbMNU2BUf\u003c/a\u003e\u003c/p\u003e\u0026mdash; Steve Francia (@spf13) \u003ca href=\"https://twitter.com/spf13/status/666616452582129664?ref_src=twsrc%5Etfw\"\u003eNovember 17, 2015\u003c/a\u003e\u003c/blockquote\u003e\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n","provider_name":"Twitter","provider_url":"https://twitter.com","type":"rich","url":"https://twitter.com/spf13/status/666616452582129664","version":"1.0","width":550}`,
   318  			`(?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&amp;ref_src=twsrc%5Etfw">#gohugo</a> <a href="https://twitter.com/hashtag/golang\?src=hash&amp;ref_src=twsrc%5Etfw">#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\?ref_src=twsrc%5Etfw">November 17, 2015</a></blockquote>\s*<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`,
   319  		},
   320  		{
   321  			map[string]interface{}{
   322  				"twitter": map[string]interface{}{
   323  					"simple": false,
   324  				},
   325  			},
   326  			`{{< tweet user="SanDiegoZoo" id="1453110110599868418" >}}`,
   327  			`{"author_name":"San Diego Boo 👻 Wildlife Alliance","author_url":"https://twitter.com/sandiegozoo","cache_age":"3153600000","height":null,"html":"\u003cblockquote class=\"twitter-tweet\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eOwl bet you\u0026#39;ll lose this staring contest 🦉 \u003ca href=\"https://t.co/eJh4f2zncC\"\u003epic.twitter.com/eJh4f2zncC\u003c/a\u003e\u003c/p\u003e\u0026mdash; San Diego Boo 👻 Wildlife Alliance (@sandiegozoo) \u003ca href=\"https://twitter.com/sandiegozoo/status/1453110110599868418?ref_src=twsrc%5Etfw\"\u003eOctober 26, 2021\u003c/a\u003e\u003c/blockquote\u003e\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n","provider_name":"Twitter","provider_url":"https://twitter.com","type":"rich","url":"https://twitter.com/sandiegozoo/status/1453110110599868418","version":"1.0","width":550}`,
   328  			`(?s)<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Owl bet you&#39;ll lose this staring contest 🦉 <a href="https://t.co/eJh4f2zncC">pic.twitter.com/eJh4f2zncC</a></p>&mdash; San Diego Boo 👻 Wildlife Alliance \(@sandiegozoo\) <a href="https://twitter.com/sandiegozoo/status/1453110110599868418\?ref_src=twsrc%5Etfw">October 26, 2021</a></blockquote>\s*<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`,
   329  		},
   330  	} {
   331  		// overload getJSON to return mock API response from Twitter
   332  		tweetFuncMap := template.FuncMap{
   333  			"getJSON": func(urlParts ...interface{}) interface{} {
   334  				var v interface{}
   335  				err := json.Unmarshal([]byte(this.resp), &v)
   336  				if err != nil {
   337  					t.Fatalf("[%d] unexpected error in json.Unmarshal: %s", i, err)
   338  					return err
   339  				}
   340  				return v
   341  			},
   342  		}
   343  
   344  		var (
   345  			cfg, fs = newTestCfg()
   346  			th      = newTestHelper(cfg, fs, t)
   347  		)
   348  
   349  		cfg.Set("privacy", this.privacy)
   350  
   351  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   352  title: Shorty
   353  ---
   354  %s`, this.in))
   355  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content }}`)
   356  
   357  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, OverloadedTemplateFuncs: tweetFuncMap}, BuildCfg{})
   358  
   359  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   360  
   361  	}
   362  }
   363  
   364  func TestShortcodeInstagram(t *testing.T) {
   365  	t.Parallel()
   366  
   367  	for i, this := range []struct {
   368  		in, hidecaption, resp, expected string
   369  	}{
   370  		{
   371  			`{{< instagram BMokmydjG-M >}}`,
   372  			`0`,
   373  			`{"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"}`,
   374  			`(?s)<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" .*defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
   375  		},
   376  		{
   377  			`{{< instagram BMokmydjG-M hidecaption >}}`,
   378  			`1`,
   379  			`{"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"}`,
   380  			`(?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>`,
   381  		},
   382  	} {
   383  		// overload getJSON to return mock API response from Instagram
   384  		instagramFuncMap := template.FuncMap{
   385  			"getJSON": func(args ...interface{}) interface{} {
   386  				headers := args[len(args)-1].(map[string]interface{})
   387  				auth := headers["Authorization"]
   388  				if auth != "Bearer dummytoken" {
   389  					return fmt.Errorf("invalid access token: %q", auth)
   390  				}
   391  				var v interface{}
   392  				err := json.Unmarshal([]byte(this.resp), &v)
   393  				if err != nil {
   394  					return fmt.Errorf("[%d] unexpected error in json.Unmarshal: %s", i, err)
   395  				}
   396  				return v
   397  			},
   398  		}
   399  
   400  		var (
   401  			cfg, fs = newTestCfg()
   402  			th      = newTestHelper(cfg, fs, t)
   403  		)
   404  
   405  		cfg.Set("services.instagram.accessToken", "dummytoken")
   406  
   407  		writeSource(t, fs, filepath.Join("content", "simple.md"), fmt.Sprintf(`---
   408  title: Shorty
   409  ---
   410  %s`, this.in))
   411  		writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .Content | safeHTML }}`)
   412  
   413  		buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, OverloadedTemplateFuncs: instagramFuncMap}, BuildCfg{})
   414  
   415  		th.assertFileContentRegexp(filepath.Join("public", "simple", "index.html"), this.expected)
   416  
   417  	}
   418  }