github.com/fretkak/mattermost-mattermost-server@v5.11.1+incompatible/utils/markdown/autolink_test.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package markdown
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  func TestParseURLAutolink(t *testing.T) {
    13  	testCases := []struct {
    14  		Description string
    15  		Input       string
    16  		Position    int
    17  		Expected    string
    18  	}{
    19  		{
    20  			Description: "no link",
    21  			Input:       "This is an :emoji:",
    22  			Position:    11,
    23  			Expected:    "",
    24  		},
    25  		{
    26  			Description: "no link 2",
    27  			Input:       "These are two things: apple and orange",
    28  			Position:    20,
    29  			Expected:    "",
    30  		},
    31  		{
    32  			Description: "link with http",
    33  			Input:       "http://example.com and some text",
    34  			Position:    4,
    35  			Expected:    "http://example.com",
    36  		},
    37  		{
    38  			Description: "link with https",
    39  			Input:       "https://example.com and some text",
    40  			Position:    5,
    41  			Expected:    "https://example.com",
    42  		},
    43  		{
    44  			Description: "link with ftp",
    45  			Input:       "ftp://example.com and some text",
    46  			Position:    3,
    47  			Expected:    "ftp://example.com",
    48  		},
    49  		{
    50  			Description: "link with a path",
    51  			Input:       "https://example.com/abcd and some text",
    52  			Position:    5,
    53  			Expected:    "https://example.com/abcd",
    54  		},
    55  		{
    56  			Description: "link with parameters",
    57  			Input:       "ftp://example.com/abcd?foo=bar and some text",
    58  			Position:    3,
    59  			Expected:    "ftp://example.com/abcd?foo=bar",
    60  		},
    61  		{
    62  			Description: "link, not at start",
    63  			Input:       "This is https://example.com and some text",
    64  			Position:    13,
    65  			Expected:    "https://example.com",
    66  		},
    67  		{
    68  			Description: "link with a path, not at start",
    69  			Input:       "This is also http://www.example.com/abcd and some text",
    70  			Position:    17,
    71  			Expected:    "http://www.example.com/abcd",
    72  		},
    73  		{
    74  			Description: "link with parameters, not at start",
    75  			Input:       "These are https://www.example.com/abcd?foo=bar and some text",
    76  			Position:    15,
    77  			Expected:    "https://www.example.com/abcd?foo=bar",
    78  		},
    79  		{
    80  			Description: "link with trailing characters",
    81  			Input:       "This is ftp://www.example.com??",
    82  			Position:    11,
    83  			Expected:    "ftp://www.example.com",
    84  		},
    85  		{
    86  			Description: "multiple links",
    87  			Input:       "This is https://example.com/abcd and ftp://www.example.com/1234",
    88  			Position:    13,
    89  			Expected:    "https://example.com/abcd",
    90  		},
    91  		{
    92  			Description: "second of multiple links",
    93  			Input:       "This is https://example.com/abcd and ftp://www.example.com/1234",
    94  			Position:    40,
    95  			Expected:    "ftp://www.example.com/1234",
    96  		},
    97  		{
    98  			Description: "link with brackets",
    99  			Input:       "Go to ftp://www.example.com/my/page_(disambiguation) and some text",
   100  			Position:    9,
   101  			Expected:    "ftp://www.example.com/my/page_(disambiguation)",
   102  		},
   103  		{
   104  			Description: "link in brackets",
   105  			Input:       "(https://www.example.com/foo/bar)",
   106  			Position:    6,
   107  			Expected:    "https://www.example.com/foo/bar",
   108  		},
   109  		{
   110  			Description: "link in underscores",
   111  			Input:       "_http://www.example.com_",
   112  			Position:    5,
   113  			Expected:    "http://www.example.com",
   114  		},
   115  		{
   116  			Description: "link in asterisks",
   117  			Input:       "This is **ftp://example.com**",
   118  			Position:    13,
   119  			Expected:    "ftp://example.com",
   120  		},
   121  		{
   122  			Description: "link in strikethrough",
   123  			Input:       "Those were ~~https://example.com~~",
   124  			Position:    18,
   125  			Expected:    "https://example.com",
   126  		},
   127  		{
   128  			Description: "link with angle brackets",
   129  			Input:       "<b>We use http://example.com</b>",
   130  			Position:    14,
   131  			Expected:    "http://example.com",
   132  		},
   133  		{
   134  			Description: "bad link protocol",
   135  			Input:       "://///",
   136  			Position:    0,
   137  			Expected:    "",
   138  		},
   139  		{
   140  			Description: "position greater than input length",
   141  			Input:       "there is no colon",
   142  			Position:    1000,
   143  			Expected:    "",
   144  		},
   145  	}
   146  
   147  	for _, testCase := range testCases {
   148  		t.Run(testCase.Description, func(t *testing.T) {
   149  			rawRange, ok := parseURLAutolink(testCase.Input, testCase.Position)
   150  
   151  			if testCase.Expected == "" {
   152  				assert.False(t, ok)
   153  				assert.Equal(t, Range{0, 0}, rawRange)
   154  			} else {
   155  				assert.True(t, ok)
   156  				assert.Equal(t, testCase.Expected, testCase.Input[rawRange.Position:rawRange.End])
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  func TestParseWWWAutolink(t *testing.T) {
   163  	testCases := []struct {
   164  		Description string
   165  		Input       string
   166  		Position    int
   167  		Expected    string
   168  	}{
   169  		{
   170  			Description: "no link",
   171  			Input:       "This is some text",
   172  			Position:    0,
   173  			Expected:    "",
   174  		},
   175  		{
   176  			Description: "link",
   177  			Input:       "www.example.com and some text",
   178  			Position:    0,
   179  			Expected:    "www.example.com",
   180  		},
   181  		{
   182  			Description: "link with a path",
   183  			Input:       "www.example.com/abcd and some text",
   184  			Position:    0,
   185  			Expected:    "www.example.com/abcd",
   186  		},
   187  		{
   188  			Description: "link with parameters",
   189  			Input:       "www.example.com/abcd?foo=bar and some text",
   190  			Position:    0,
   191  			Expected:    "www.example.com/abcd?foo=bar",
   192  		},
   193  		{
   194  			Description: "link, not at start",
   195  			Input:       "This is www.example.com and some text",
   196  			Position:    8,
   197  			Expected:    "www.example.com",
   198  		},
   199  		{
   200  			Description: "link with a path, not at start",
   201  			Input:       "This is also www.example.com/abcd and some text",
   202  			Position:    13,
   203  			Expected:    "www.example.com/abcd",
   204  		},
   205  		{
   206  			Description: "link with parameters, not at start",
   207  			Input:       "These are www.example.com/abcd?foo=bar and some text",
   208  			Position:    10,
   209  			Expected:    "www.example.com/abcd?foo=bar",
   210  		},
   211  		{
   212  			Description: "link with trailing characters",
   213  			Input:       "This is www.example.com??",
   214  			Position:    8,
   215  			Expected:    "www.example.com",
   216  		},
   217  		{
   218  			Description: "link after current position",
   219  			Input:       "This is some text and www.example.com",
   220  			Position:    0,
   221  			Expected:    "",
   222  		},
   223  		{
   224  			Description: "multiple links",
   225  			Input:       "This is www.example.com/abcd and www.example.com/1234",
   226  			Position:    8,
   227  			Expected:    "www.example.com/abcd",
   228  		},
   229  		{
   230  			Description: "multiple links 2",
   231  			Input:       "This is www.example.com/abcd and www.example.com/1234",
   232  			Position:    33,
   233  			Expected:    "www.example.com/1234",
   234  		},
   235  		{
   236  			Description: "link with brackets",
   237  			Input:       "Go to www.example.com/my/page_(disambiguation) and some text",
   238  			Position:    6,
   239  			Expected:    "www.example.com/my/page_(disambiguation)",
   240  		},
   241  		{
   242  			Description: "link following other letters",
   243  			Input:       "aaawww.example.com and some text",
   244  			Position:    3,
   245  			Expected:    "",
   246  		},
   247  		{
   248  			Description: "link in brackets",
   249  			Input:       "(www.example.com)",
   250  			Position:    1,
   251  			Expected:    "www.example.com",
   252  		},
   253  		{
   254  			Description: "link in underscores",
   255  			Input:       "_www.example.com_",
   256  			Position:    1,
   257  			Expected:    "www.example.com",
   258  		},
   259  		{
   260  			Description: "link in asterisks",
   261  			Input:       "This is **www.example.com**",
   262  			Position:    10,
   263  			Expected:    "www.example.com",
   264  		},
   265  		{
   266  			Description: "link in strikethrough",
   267  			Input:       "Those were ~~www.example.com~~",
   268  			Position:    13,
   269  			Expected:    "www.example.com",
   270  		},
   271  		{
   272  			Description: "using www1",
   273  			Input:       "Our backup site is at www1.example.com/foo",
   274  			Position:    22,
   275  			Expected:    "www1.example.com/foo",
   276  		},
   277  		{
   278  			Description: "link with angle brackets",
   279  			Input:       "<b>We use www2.example.com</b>",
   280  			Position:    10,
   281  			Expected:    "www2.example.com",
   282  		},
   283  	}
   284  
   285  	for _, testCase := range testCases {
   286  		t.Run(testCase.Description, func(t *testing.T) {
   287  			rawRange, ok := parseWWWAutolink(testCase.Input, testCase.Position)
   288  
   289  			if testCase.Expected == "" {
   290  				assert.False(t, ok)
   291  				assert.Equal(t, Range{0, 0}, rawRange)
   292  			} else {
   293  				assert.True(t, ok)
   294  				assert.Equal(t, testCase.Expected, testCase.Input[rawRange.Position:rawRange.End])
   295  			}
   296  		})
   297  	}
   298  }
   299  
   300  func TestTrimTrailingCharactersFromLink(t *testing.T) {
   301  	testCases := []struct {
   302  		Input       string
   303  		Start       int
   304  		End         int
   305  		ExpectedEnd int
   306  	}{
   307  		{
   308  			Input:       "http://www.example.com",
   309  			ExpectedEnd: 22,
   310  		},
   311  		{
   312  			Input:       "http://www.example.com/abcd",
   313  			ExpectedEnd: 27,
   314  		},
   315  		{
   316  			Input:       "http://www.example.com/abcd/",
   317  			ExpectedEnd: 28,
   318  		},
   319  		{
   320  			Input:       "http://www.example.com/1234",
   321  			ExpectedEnd: 27,
   322  		},
   323  		{
   324  			Input:       "http://www.example.com/abcd?foo=bar",
   325  			ExpectedEnd: 35,
   326  		},
   327  		{
   328  			Input:       "http://www.example.com/abcd#heading",
   329  			ExpectedEnd: 35,
   330  		},
   331  		{
   332  			Input:       "http://www.example.com.",
   333  			ExpectedEnd: 22,
   334  		},
   335  		{
   336  			Input:       "http://www.example.com,",
   337  			ExpectedEnd: 22,
   338  		},
   339  		{
   340  			Input:       "http://www.example.com?",
   341  			ExpectedEnd: 22,
   342  		},
   343  		{
   344  			Input:       "http://www.example.com)",
   345  			ExpectedEnd: 22,
   346  		},
   347  		{
   348  			Input:       "http://www.example.com",
   349  			ExpectedEnd: 22,
   350  		},
   351  		{
   352  			Input:       "https://en.wikipedia.org/wiki/Dolphin_(disambiguation)",
   353  			ExpectedEnd: 54,
   354  		},
   355  		{
   356  			Input:       "https://en.wikipedia.org/wiki/Dolphin_(disambiguation",
   357  			ExpectedEnd: 53,
   358  		},
   359  		{
   360  			Input:       "https://en.wikipedia.org/wiki/Dolphin_(disambiguation))",
   361  			ExpectedEnd: 54,
   362  		},
   363  		{
   364  			Input:       "https://en.wikipedia.org/wiki/Dolphin_(disambiguation)_(disambiguation)",
   365  			ExpectedEnd: 71,
   366  		},
   367  		{
   368  			Input:       "https://en.wikipedia.org/wiki/Dolphin_(disambiguation_(disambiguation))",
   369  			ExpectedEnd: 71,
   370  		},
   371  		{
   372  			Input:       "http://www.example.com&quot;",
   373  			ExpectedEnd: 22,
   374  		},
   375  		{
   376  			Input:       "this is a sentence containing http://www.example.com in it",
   377  			Start:       30,
   378  			End:         52,
   379  			ExpectedEnd: 52,
   380  		},
   381  		{
   382  			Input:       "this is a sentence containing http://www.example.com???",
   383  			Start:       30,
   384  			End:         55,
   385  			ExpectedEnd: 52,
   386  		},
   387  		{
   388  			Input:       "http://google.com/å",
   389  			ExpectedEnd: len("http://google.com/å"),
   390  		},
   391  		{
   392  			Input:       "http://google.com/å...",
   393  			ExpectedEnd: len("http://google.com/å"),
   394  		},
   395  		{
   396  			Input:       "This is http://google.com/å, a link, and http://google.com/å",
   397  			Start:       8,
   398  			End:         len("This is http://google.com/å,"),
   399  			ExpectedEnd: len("This is http://google.com/å"),
   400  		},
   401  		{
   402  			Input:       "This is http://google.com/å, a link, and http://google.com/å",
   403  			Start:       41,
   404  			End:         len("This is http://google.com/å, a link, and http://google.com/å"),
   405  			ExpectedEnd: len("This is http://google.com/å, a link, and http://google.com/å"),
   406  		},
   407  		{
   408  			Input:       "This is http://google.com/å, a link, and http://google.com/å.",
   409  			Start:       41,
   410  			End:         len("This is http://google.com/å, a link, and http://google.com/å."),
   411  			ExpectedEnd: len("This is http://google.com/å, a link, and http://google.com/å"),
   412  		},
   413  		{
   414  			Input:       "http://🍄.ga/ http://x🍄.ga/",
   415  			Start:       0,
   416  			End:         len("http://🍄.ga/"),
   417  			ExpectedEnd: len("http://🍄.ga/"),
   418  		},
   419  		{
   420  			Input:       "http://🍄.ga/ http://x🍄.ga/",
   421  			Start:       len("http://🍄.ga/ "),
   422  			End:         len("http://🍄.ga/ http://x🍄.ga/"),
   423  			ExpectedEnd: len("http://🍄.ga/ http://x🍄.ga/"),
   424  		},
   425  	}
   426  
   427  	for _, testCase := range testCases {
   428  		t.Run(testCase.Input, func(t *testing.T) {
   429  			if testCase.End == 0 {
   430  				testCase.End = len(testCase.Input) - testCase.Start
   431  			}
   432  
   433  			assert.Equal(t, testCase.ExpectedEnd, trimTrailingCharactersFromLink(testCase.Input, testCase.Start, testCase.End))
   434  		})
   435  	}
   436  }
   437  
   438  func TestAutolinking(t *testing.T) {
   439  	// These tests are adapted from https://github.com/mattermost/commonmark.js/test/mattermost.txt.
   440  	// It is missing tests for:
   441  	// 1. Links surrounded by emphasis (emphasis not implemented on the server)
   442  	// 2. IPv6 addresses (not implemented on the server or by GitHub)
   443  	// 3. Custom URL schemes (not implemented)
   444  
   445  	for name, tc := range map[string]struct {
   446  		Markdown     string
   447  		ExpectedHTML string
   448  	}{
   449  		"valid-link-1": {
   450  			Markdown:     `http://example.com`,
   451  			ExpectedHTML: `<p><a href="http://example.com">http://example.com</a></p>`,
   452  		},
   453  		"valid-link-2": {
   454  			Markdown:     `https://example.com`,
   455  			ExpectedHTML: `<p><a href="https://example.com">https://example.com</a></p>`,
   456  		},
   457  		"valid-link-3": {
   458  			Markdown:     `ftp://example.com`,
   459  			ExpectedHTML: `<p><a href="ftp://example.com">ftp://example.com</a></p>`,
   460  		},
   461  		// "valid-link-4": {
   462  		// 	Markdown:     `ts3server://example.com?port=9001`,
   463  		// 	ExpectedHTML: `<p><a href="ts3server://example.com?port=9001">ts3server://example.com?port=9001</a></p>`,
   464  		// },
   465  		"valid-link-5": {
   466  			Markdown:     `www.example.com`,
   467  			ExpectedHTML: `<p><a href="http://www.example.com">www.example.com</a></p>`,
   468  		},
   469  		"valid-link-6": {
   470  			Markdown:     `www.example.com/index`,
   471  			ExpectedHTML: `<p><a href="http://www.example.com/index">www.example.com/index</a></p>`,
   472  		},
   473  		"valid-link-7": {
   474  			Markdown:     `www.example.com/index.html`,
   475  			ExpectedHTML: `<p><a href="http://www.example.com/index.html">www.example.com/index.html</a></p>`,
   476  		},
   477  		"valid-link-8": {
   478  			Markdown:     `http://example.com/index/sub`,
   479  			ExpectedHTML: `<p><a href="http://example.com/index/sub">http://example.com/index/sub</a></p>`,
   480  		},
   481  		"valid-link-9": {
   482  			Markdown:     `www1.example.com`,
   483  			ExpectedHTML: `<p><a href="http://www1.example.com">www1.example.com</a></p>`,
   484  		},
   485  		"valid-link-10": {
   486  			Markdown:     `https://en.wikipedia.org/wiki/URLs#Syntax`,
   487  			ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/URLs#Syntax">https://en.wikipedia.org/wiki/URLs#Syntax</a></p>`,
   488  		},
   489  		"valid-link-11": {
   490  			Markdown:     `https://groups.google.com/forum/#!msg`,
   491  			ExpectedHTML: `<p><a href="https://groups.google.com/forum/#!msg">https://groups.google.com/forum/#!msg</a></p>`,
   492  		},
   493  		"valid-link-12": {
   494  			Markdown:     `www.example.com/index?params=1`,
   495  			ExpectedHTML: `<p><a href="http://www.example.com/index?params=1">www.example.com/index?params=1</a></p>`,
   496  		},
   497  		"valid-link-13": {
   498  			Markdown:     `www.example.com/index?params=1&other=2`,
   499  			ExpectedHTML: `<p><a href="http://www.example.com/index?params=1&amp;other=2">www.example.com/index?params=1&amp;other=2</a></p>`,
   500  		},
   501  		"valid-link-14": {
   502  			Markdown:     `www.example.com/index?params=1;other=2`,
   503  			ExpectedHTML: `<p><a href="http://www.example.com/index?params=1;other=2">www.example.com/index?params=1;other=2</a></p>`,
   504  		},
   505  		"valid-link-15": {
   506  			Markdown:     `http://www.example.com/_/page`,
   507  			ExpectedHTML: `<p><a href="http://www.example.com/_/page">http://www.example.com/_/page</a></p>`,
   508  		},
   509  		"valid-link-16": {
   510  			Markdown:     `https://en.wikipedia.org/wiki/🐬`,
   511  			ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/%F0%9F%90%AC">https://en.wikipedia.org/wiki/🐬</a></p>`,
   512  		},
   513  		"valid-link-17": {
   514  			Markdown:     `http://✪df.ws/1234`,
   515  			ExpectedHTML: `<p><a href="http://%E2%9C%AAdf.ws/1234">http://✪df.ws/1234</a></p>`,
   516  		},
   517  		"valid-link-18": {
   518  			Markdown:     `https://groups.google.com/forum/#!msg`,
   519  			ExpectedHTML: `<p><a href="https://groups.google.com/forum/#!msg">https://groups.google.com/forum/#!msg</a></p>`,
   520  		},
   521  		"valid-link-19": {
   522  			Markdown:     `https://пример.срб/пример-26/`,
   523  			ExpectedHTML: `<p><a href="https://%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%81%D1%80%D0%B1/%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-26/">https://пример.срб/пример-26/</a></p>`,
   524  		},
   525  		"valid-link-20": {
   526  			Markdown:     `mailto://test@example.com`,
   527  			ExpectedHTML: `<p><a href="mailto://test@example.com">mailto://test@example.com</a></p>`,
   528  		},
   529  		"valid-link-21": {
   530  			Markdown:     `tel://555-123-4567`,
   531  			ExpectedHTML: `<p><a href="tel://555-123-4567">tel://555-123-4567</a></p>`,
   532  		},
   533  
   534  		"ip-address-1": {
   535  			Markdown:     `http://127.0.0.1`,
   536  			ExpectedHTML: `<p><a href="http://127.0.0.1">http://127.0.0.1</a></p>`,
   537  		},
   538  		"ip-address-2": {
   539  			Markdown:     `http://192.168.1.1:4040`,
   540  			ExpectedHTML: `<p><a href="http://192.168.1.1:4040">http://192.168.1.1:4040</a></p>`,
   541  		},
   542  		"ip-address-3": {
   543  			Markdown:     `http://username:password@127.0.0.1`,
   544  			ExpectedHTML: `<p><a href="http://username:password@127.0.0.1">http://username:password@127.0.0.1</a></p>`,
   545  		},
   546  		"ip-address-4": {
   547  			Markdown:     `http://username:password@[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80`,
   548  			ExpectedHTML: `<p><a href="http://username:password@%5B2001:0:5ef5:79fb:303a:62d5:3312:ff42%5D:80">http://username:password@[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80</a></p>`,
   549  		},
   550  
   551  		"link-with-brackets-1": {
   552  			Markdown:     `https://en.wikipedia.org/wiki/Rendering_(computer_graphics)`,
   553  			ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/Rendering_(computer_graphics)">https://en.wikipedia.org/wiki/Rendering_(computer_graphics)</a></p>`,
   554  		},
   555  		"link-with-brackets-2": {
   556  			Markdown:     `http://example.com/more_(than)_one_(parens)`,
   557  			ExpectedHTML: `<p><a href="http://example.com/more_(than)_one_(parens)">http://example.com/more_(than)_one_(parens)</a></p>`,
   558  		},
   559  		"link-with-brackets-3": {
   560  			Markdown:     `http://example.com/(something)?after=parens`,
   561  			ExpectedHTML: `<p><a href="http://example.com/(something)?after=parens">http://example.com/(something)?after=parens</a></p>`,
   562  		},
   563  		"link-with-brackets-4": {
   564  			Markdown:     `http://foo.com/unicode_(✪)_in_parens`,
   565  			ExpectedHTML: `<p><a href="http://foo.com/unicode_(%E2%9C%AA)_in_parens">http://foo.com/unicode_(✪)_in_parens</a></p>`,
   566  		},
   567  
   568  		"inside-another-link-1": {
   569  			Markdown:     `[www.example.com](https://example.com)`,
   570  			ExpectedHTML: `<p><a href="https://example.com">www.example.com</a></p>`,
   571  		},
   572  		"inside-another-link-2": {
   573  			Markdown:     `[http://www.example.com](https://example.com)`,
   574  			ExpectedHTML: `<p><a href="https://example.com">http://www.example.com</a></p>`,
   575  		},
   576  
   577  		"link-in-sentence-1": {
   578  			Markdown:     `(http://example.com)`,
   579  			ExpectedHTML: `<p>(<a href="http://example.com">http://example.com</a>)</p>`,
   580  		},
   581  		"link-in-sentence-2": {
   582  			Markdown:     `(see http://example.com)`,
   583  			ExpectedHTML: `<p>(see <a href="http://example.com">http://example.com</a>)</p>`,
   584  		},
   585  		"link-in-sentence-3": {
   586  			Markdown:     `(http://example.com watch this)`,
   587  			ExpectedHTML: `<p>(<a href="http://example.com">http://example.com</a> watch this)</p>`,
   588  		},
   589  		"link-in-sentence-4": {
   590  			Markdown:     `This is a sentence with a http://example.com in it.`,
   591  			ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com">http://example.com</a> in it.</p>`,
   592  		},
   593  		"link-in-sentence-5": {
   594  			Markdown:     `This is a sentence with a [link](http://example.com) in it.`,
   595  			ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com">link</a> in it.</p>`,
   596  		},
   597  		"link-in-sentence-6": {
   598  			Markdown:     `This is a sentence with a http://example.com/_/underscore in it.`,
   599  			ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com/_/underscore">http://example.com/_/underscore</a> in it.</p>`,
   600  		},
   601  		"link-in-sentence-7": {
   602  			Markdown:     `This is a sentence with a link (http://example.com) in it.`,
   603  			ExpectedHTML: `<p>This is a sentence with a link (<a href="http://example.com">http://example.com</a>) in it.</p>`,
   604  		},
   605  		"link-in-sentence-8": {
   606  			Markdown:     `This is a sentence with a (https://en.wikipedia.org/wiki/Rendering_(computer_graphics)) in it.`,
   607  			ExpectedHTML: `<p>This is a sentence with a (<a href="https://en.wikipedia.org/wiki/Rendering_(computer_graphics)">https://en.wikipedia.org/wiki/Rendering_(computer_graphics)</a>) in it.</p>`,
   608  		},
   609  		"link-in-sentence-9": {
   610  			Markdown:     `This is a sentence with a http://192.168.1.1:4040 in it.`,
   611  			ExpectedHTML: `<p>This is a sentence with a <a href="http://192.168.1.1:4040">http://192.168.1.1:4040</a> in it.</p>`,
   612  		},
   613  		"link-in-sentence-10": {
   614  			Markdown:     `This is a link to http://example.com.`,
   615  			ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>.</p>`,
   616  		},
   617  		"link-in-sentence-11": {
   618  			Markdown:     `This is a link to http://example.com*`,
   619  			ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>*</p>`,
   620  		},
   621  		"link-in-sentence-12": {
   622  			Markdown:     `This is a link to http://example.com_`,
   623  			ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>_</p>`,
   624  		},
   625  		"link-in-sentence-13": {
   626  			Markdown:     `This is a link containing http://example.com/something?with,commas,in,url, but not at the end`,
   627  			ExpectedHTML: `<p>This is a link containing <a href="http://example.com/something?with,commas,in,url">http://example.com/something?with,commas,in,url</a>, but not at the end</p>`,
   628  		},
   629  		"link-in-sentence-14": {
   630  			Markdown:     `This is a question about a link http://example.com?`,
   631  			ExpectedHTML: `<p>This is a question about a link <a href="http://example.com">http://example.com</a>?</p>`,
   632  		},
   633  
   634  		"plt-7250-link-with-trailing-periods-1": {
   635  			Markdown:     `http://example.com.`,
   636  			ExpectedHTML: `<p><a href="http://example.com">http://example.com</a>.</p>`,
   637  		},
   638  		"plt-7250-link-with-trailing-periods-2": {
   639  			Markdown:     `http://example.com...`,
   640  			ExpectedHTML: `<p><a href="http://example.com">http://example.com</a>...</p>`,
   641  		},
   642  		"plt-7250-link-with-trailing-periods-3": {
   643  			Markdown:     `http://example.com/foo.`,
   644  			ExpectedHTML: `<p><a href="http://example.com/foo">http://example.com/foo</a>.</p>`,
   645  		},
   646  		"plt-7250-link-with-trailing-periods-4": {
   647  			Markdown:     `http://example.com/foo...`,
   648  			ExpectedHTML: `<p><a href="http://example.com/foo">http://example.com/foo</a>...</p>`,
   649  		},
   650  		"plt-7250-link-with-trailing-periods-5": {
   651  			Markdown:     `http://example.com/foo.bar`,
   652  			ExpectedHTML: `<p><a href="http://example.com/foo.bar">http://example.com/foo.bar</a></p>`,
   653  		},
   654  		"plt-7250-link-with-trailing-periods-6": {
   655  			Markdown:     `http://example.com/foo...bar`,
   656  			ExpectedHTML: `<p><a href="http://example.com/foo...bar">http://example.com/foo...bar</a></p>`,
   657  		},
   658  
   659  		"rn-319-www-link-as-part-of-word-1": {
   660  			Markdown:     `testwww.example.com`,
   661  			ExpectedHTML: `<p>testwww.example.com</p>`,
   662  		},
   663  
   664  		"mm-10180-link-containing-period-followed-by-non-letter-1": {
   665  			Markdown:     `https://example.com/123.+Pagetitle`,
   666  			ExpectedHTML: `<p><a href="https://example.com/123.+Pagetitle">https://example.com/123.+Pagetitle</a></p>`,
   667  		},
   668  		"mm-10180-link-containing-period-followed-by-non-letter-2": {
   669  			Markdown:     `https://example.com/123.?Pagetitle`,
   670  			ExpectedHTML: `<p><a href="https://example.com/123.?Pagetitle">https://example.com/123.?Pagetitle</a></p>`,
   671  		},
   672  		"mm-10180-link-containing-period-followed-by-non-letter-3": {
   673  			Markdown:     `https://example.com/123.-Pagetitle`,
   674  			ExpectedHTML: `<p><a href="https://example.com/123.-Pagetitle">https://example.com/123.-Pagetitle</a></p>`,
   675  		},
   676  		"mm-10180-link-containing-period-followed-by-non-letter-4": {
   677  			Markdown:     `https://example.com/123._Pagetitle`,
   678  			ExpectedHTML: `<p><a href="https://example.com/123._Pagetitle">https://example.com/123._Pagetitle</a></p>`,
   679  		},
   680  		"mm-10180-link-containing-period-followed-by-non-letter-5": {
   681  			Markdown:     `https://example.com/123.+`,
   682  			ExpectedHTML: `<p><a href="https://example.com/123.+">https://example.com/123.+</a></p>`,
   683  		},
   684  		"mm-10180-link-containing-period-followed-by-non-letter-6": {
   685  			Markdown:     `https://example.com/123.?`,
   686  			ExpectedHTML: `<p><a href="https://example.com/123">https://example.com/123</a>.?</p>`,
   687  		},
   688  		"mm-10180-link-containing-period-followed-by-non-letter-7": {
   689  			Markdown:     `https://example.com/123.-`,
   690  			ExpectedHTML: `<p><a href="https://example.com/123.-">https://example.com/123.-</a></p>`,
   691  		},
   692  		"mm-10180-link-containing-period-followed-by-non-letter-8": {
   693  			Markdown:     `https://example.com/123._`,
   694  			ExpectedHTML: `<p><a href="https://example.com/123">https://example.com/123</a>._</p>`,
   695  		},
   696  	} {
   697  		t.Run(name, func(t *testing.T) {
   698  			assert.Equal(t, tc.ExpectedHTML, RenderHTML(tc.Markdown))
   699  		})
   700  	}
   701  }