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