hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

embedded_shortcodes_test.go (25911B)

    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]any
  298 		in, resp, expected string
  299 	}{
  300 		{
  301 			map[string]any{
  302 				"twitter": map[string]any{
  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]any{
  312 				"twitter": map[string]any{
  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]any{
  322 				"twitter": map[string]any{
  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 ...any) any {
  334 				var v any
  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 ...any) any {
  386 				headers := args[len(args)-1].(map[string]any)
  387 				auth := headers["Authorization"]
  388 				if auth != "Bearer dummytoken" {
  389 					return fmt.Errorf("invalid access token: %q", auth)
  390 				}
  391 				var v any
  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 }