code.gitea.io/gitea@v1.22.3/modules/issue/template/template_test.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package template
     5  
     6  import (
     7  	"net/url"
     8  	"testing"
     9  
    10  	"code.gitea.io/gitea/modules/json"
    11  	api "code.gitea.io/gitea/modules/structs"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestValidate(t *testing.T) {
    18  	tests := []struct {
    19  		name     string
    20  		filename string
    21  		content  string
    22  		want     *api.IssueTemplate
    23  		wantErr  string
    24  	}{
    25  		{
    26  			name:    "miss name",
    27  			content: ``,
    28  			wantErr: "'name' is required",
    29  		},
    30  		{
    31  			name: "miss about",
    32  			content: `
    33  name: "test"
    34  `,
    35  			wantErr: "'about' is required",
    36  		},
    37  		{
    38  			name: "miss body",
    39  			content: `
    40  name: "test"
    41  about: "this is about"
    42  `,
    43  			wantErr: "'body' is required",
    44  		},
    45  		{
    46  			name: "markdown miss value",
    47  			content: `
    48  name: "test"
    49  about: "this is about"
    50  body:
    51    - type: "markdown"
    52  `,
    53  			wantErr: "body[0](markdown): 'value' is required",
    54  		},
    55  		{
    56  			name: "markdown invalid value",
    57  			content: `
    58  name: "test"
    59  about: "this is about"
    60  body:
    61    - type: "markdown"
    62      attributes:
    63        value: true
    64  `,
    65  			wantErr: "body[0](markdown): 'value' should be a string",
    66  		},
    67  		{
    68  			name: "markdown empty value",
    69  			content: `
    70  name: "test"
    71  about: "this is about"
    72  body:
    73    - type: "markdown"
    74      attributes:
    75        value: ""
    76  `,
    77  			wantErr: "body[0](markdown): 'value' is required",
    78  		},
    79  		{
    80  			name: "textarea invalid id",
    81  			content: `
    82  name: "test"
    83  about: "this is about"
    84  body:
    85    - type: "textarea"
    86      id: "?"
    87  `,
    88  			wantErr: "body[0](textarea): 'id' should contain only alphanumeric, '-' and '_'",
    89  		},
    90  		{
    91  			name: "textarea miss label",
    92  			content: `
    93  name: "test"
    94  about: "this is about"
    95  body:
    96    - type: "textarea"
    97      id: "1"
    98  `,
    99  			wantErr: "body[0](textarea): 'label' is required",
   100  		},
   101  		{
   102  			name: "textarea conflict id",
   103  			content: `
   104  name: "test"
   105  about: "this is about"
   106  body:
   107    - type: "textarea"
   108      id: "1"
   109      attributes:
   110        label: "a"
   111    - type: "textarea"
   112      id: "1"
   113      attributes:
   114        label: "b"
   115  `,
   116  			wantErr: "body[1](textarea): 'id' should be unique",
   117  		},
   118  		{
   119  			name: "textarea invalid description",
   120  			content: `
   121  name: "test"
   122  about: "this is about"
   123  body:
   124    - type: "textarea"
   125      id: "1"
   126      attributes:
   127        label: "a"
   128        description: true
   129  `,
   130  			wantErr: "body[0](textarea): 'description' should be a string",
   131  		},
   132  		{
   133  			name: "textarea invalid required",
   134  			content: `
   135  name: "test"
   136  about: "this is about"
   137  body:
   138    - type: "textarea"
   139      id: "1"
   140      attributes:
   141        label: "a"
   142      validations:
   143        required: "on"
   144  `,
   145  			wantErr: "body[0](textarea): 'required' should be a bool",
   146  		},
   147  		{
   148  			name: "input invalid description",
   149  			content: `
   150  name: "test"
   151  about: "this is about"
   152  body:
   153    - type: "input"
   154      id: "1"
   155      attributes:
   156        label: "a"
   157        description: true
   158  `,
   159  			wantErr: "body[0](input): 'description' should be a string",
   160  		},
   161  		{
   162  			name: "input invalid is_number",
   163  			content: `
   164  name: "test"
   165  about: "this is about"
   166  body:
   167    - type: "input"
   168      id: "1"
   169      attributes:
   170        label: "a"
   171      validations:
   172        is_number: "yes"
   173  `,
   174  			wantErr: "body[0](input): 'is_number' should be a bool",
   175  		},
   176  		{
   177  			name: "input invalid regex",
   178  			content: `
   179  name: "test"
   180  about: "this is about"
   181  body:
   182    - type: "input"
   183      id: "1"
   184      attributes:
   185        label: "a"
   186      validations:
   187        regex: true
   188  `,
   189  			wantErr: "body[0](input): 'regex' should be a string",
   190  		},
   191  		{
   192  			name: "dropdown invalid description",
   193  			content: `
   194  name: "test"
   195  about: "this is about"
   196  body:
   197    - type: "dropdown"
   198      id: "1"
   199      attributes:
   200        label: "a"
   201        description: true
   202  `,
   203  			wantErr: "body[0](dropdown): 'description' should be a string",
   204  		},
   205  		{
   206  			name: "dropdown invalid multiple",
   207  			content: `
   208  name: "test"
   209  about: "this is about"
   210  body:
   211    - type: "dropdown"
   212      id: "1"
   213      attributes:
   214        label: "a"
   215        multiple: "on"
   216  `,
   217  			wantErr: "body[0](dropdown): 'multiple' should be a bool",
   218  		},
   219  		{
   220  			name: "checkboxes invalid description",
   221  			content: `
   222  name: "test"
   223  about: "this is about"
   224  body:
   225    - type: "checkboxes"
   226      id: "1"
   227      attributes:
   228        label: "a"
   229        description: true
   230  `,
   231  			wantErr: "body[0](checkboxes): 'description' should be a string",
   232  		},
   233  		{
   234  			name: "invalid type",
   235  			content: `
   236  name: "test"
   237  about: "this is about"
   238  body:
   239    - type: "video"
   240      id: "1"
   241      attributes:
   242        label: "a"
   243  `,
   244  			wantErr: "body[0](video): unknown type",
   245  		},
   246  		{
   247  			name: "dropdown miss options",
   248  			content: `
   249  name: "test"
   250  about: "this is about"
   251  body:
   252    - type: "dropdown"
   253      id: "1"
   254      attributes:
   255        label: "a"
   256  `,
   257  			wantErr: "body[0](dropdown): 'options' is required and should be a array",
   258  		},
   259  		{
   260  			name: "dropdown invalid options",
   261  			content: `
   262  name: "test"
   263  about: "this is about"
   264  body:
   265    - type: "dropdown"
   266      id: "1"
   267      attributes:
   268        label: "a"
   269        options:
   270          - "a"
   271          - true
   272  `,
   273  			wantErr: "body[0](dropdown), option[1]: should be a string",
   274  		},
   275  		{
   276  			name: "checkboxes invalid options",
   277  			content: `
   278  name: "test"
   279  about: "this is about"
   280  body:
   281    - type: "checkboxes"
   282      id: "1"
   283      attributes:
   284        label: "a"
   285        options:
   286          - "a"
   287          - true
   288  `,
   289  			wantErr: "body[0](checkboxes), option[0]: should be a dictionary",
   290  		},
   291  		{
   292  			name: "checkboxes option miss label",
   293  			content: `
   294  name: "test"
   295  about: "this is about"
   296  body:
   297    - type: "checkboxes"
   298      id: "1"
   299      attributes:
   300        label: "a"
   301        options:
   302          - required: true
   303  `,
   304  			wantErr: "body[0](checkboxes), option[0]: 'label' is required and should be a string",
   305  		},
   306  		{
   307  			name: "checkboxes option invalid required",
   308  			content: `
   309  name: "test"
   310  about: "this is about"
   311  body:
   312    - type: "checkboxes"
   313      id: "1"
   314      attributes:
   315        label: "a"
   316        options:
   317          - label: "a"
   318            required: "on"
   319  `,
   320  			wantErr: "body[0](checkboxes), option[0]: 'required' should be a bool",
   321  		},
   322  		{
   323  			name: "field is required but hidden",
   324  			content: `
   325  name: "test"
   326  about: "this is about"
   327  body:
   328    - type: "input"
   329      id: "1"
   330      attributes:
   331        label: "a"
   332      validations:
   333        required: true
   334      visible: [content]
   335  `,
   336  			wantErr: "body[0](input): can not require a hidden field",
   337  		},
   338  		{
   339  			name: "checkboxes is required but hidden",
   340  			content: `
   341  name: "test"
   342  about: "this is about"
   343  body:
   344    - type: checkboxes
   345      id: "1"
   346      attributes:
   347        label: Label of checkboxes
   348        description: Description of checkboxes
   349        options:
   350          - label: Option 1
   351            required: false
   352          - label: Required and hidden
   353            required: true
   354            visible: [content]
   355  `,
   356  			wantErr: "body[0](checkboxes), option[1]: can not require a hidden checkbox",
   357  		},
   358  		{
   359  			name: "dropdown default is not an integer",
   360  			content: `
   361  name: "test"
   362  about: "this is about"
   363  body:
   364    - type: dropdown
   365      id: "1"
   366      attributes:
   367        label: Label of dropdown
   368        description: Description of dropdown
   369        multiple: true
   370        options:
   371          - Option 1 of dropdown
   372          - Option 2 of dropdown
   373          - Option 3 of dropdown
   374        default: "def"
   375      validations:
   376        required: true
   377  `,
   378  			wantErr: "body[0](dropdown): 'default' should be an int",
   379  		},
   380  		{
   381  			name: "dropdown default is out of range",
   382  			content: `
   383  name: "test"
   384  about: "this is about"
   385  body:
   386    - type: dropdown
   387      id: "1"
   388      attributes:
   389        label: Label of dropdown
   390        description: Description of dropdown
   391        multiple: true
   392        options:
   393          - Option 1 of dropdown
   394          - Option 2 of dropdown
   395          - Option 3 of dropdown
   396        default: 3
   397      validations:
   398        required: true
   399  `,
   400  			wantErr: "body[0](dropdown): the value of 'default' is out of range",
   401  		},
   402  		{
   403  			name: "dropdown without default is valid",
   404  			content: `
   405  name: "test"
   406  about: "this is about"
   407  body:
   408    - type: dropdown
   409      id: "1"
   410      attributes:
   411        label: Label of dropdown
   412        description: Description of dropdown
   413        multiple: true
   414        options:
   415          - Option 1 of dropdown
   416          - Option 2 of dropdown
   417          - Option 3 of dropdown
   418      validations:
   419        required: true
   420  `,
   421  			want: &api.IssueTemplate{
   422  				Name:  "test",
   423  				About: "this is about",
   424  				Fields: []*api.IssueFormField{
   425  					{
   426  						Type: "dropdown",
   427  						ID:   "1",
   428  						Attributes: map[string]any{
   429  							"label":       "Label of dropdown",
   430  							"description": "Description of dropdown",
   431  							"multiple":    true,
   432  							"options": []any{
   433  								"Option 1 of dropdown",
   434  								"Option 2 of dropdown",
   435  								"Option 3 of dropdown",
   436  							},
   437  						},
   438  						Validations: map[string]any{
   439  							"required": true,
   440  						},
   441  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
   442  					},
   443  				},
   444  				FileName: "test.yaml",
   445  			},
   446  			wantErr: "",
   447  		},
   448  		{
   449  			name: "valid",
   450  			content: `
   451  name: Name
   452  title: Title
   453  about: About
   454  labels: ["label1", "label2"]
   455  ref: Ref
   456  body:
   457    - type: markdown
   458      id: id1
   459      attributes:
   460        value: Value of the markdown
   461    - type: textarea
   462      id: id2
   463      attributes:
   464        label: Label of textarea
   465        description: Description of textarea
   466        placeholder: Placeholder of textarea
   467        value: Value of textarea
   468        render: bash
   469      validations:
   470        required: true
   471    - type: input
   472      id: id3
   473      attributes:
   474        label: Label of input
   475        description: Description of input
   476        placeholder: Placeholder of input
   477        value: Value of input
   478      validations:
   479        required: true
   480        is_number: true
   481        regex: "[a-zA-Z0-9]+"
   482    - type: dropdown
   483      id: id4
   484      attributes:
   485        label: Label of dropdown
   486        description: Description of dropdown
   487        multiple: true
   488        options:
   489          - Option 1 of dropdown
   490          - Option 2 of dropdown
   491          - Option 3 of dropdown
   492        default: 1
   493      validations:
   494        required: true
   495    - type: checkboxes
   496      id: id5
   497      attributes:
   498        label: Label of checkboxes
   499        description: Description of checkboxes
   500        options:
   501          - label: Option 1 of checkboxes
   502            required: true
   503          - label: Option 2 of checkboxes
   504            required: false
   505          - label: Hidden Option 3 of checkboxes
   506            visible: [content]
   507          - label: Required but not submitted
   508            required: true
   509            visible: [form]
   510  `,
   511  			want: &api.IssueTemplate{
   512  				Name:   "Name",
   513  				Title:  "Title",
   514  				About:  "About",
   515  				Labels: []string{"label1", "label2"},
   516  				Ref:    "Ref",
   517  				Fields: []*api.IssueFormField{
   518  					{
   519  						Type: "markdown",
   520  						ID:   "id1",
   521  						Attributes: map[string]any{
   522  							"value": "Value of the markdown",
   523  						},
   524  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
   525  					},
   526  					{
   527  						Type: "textarea",
   528  						ID:   "id2",
   529  						Attributes: map[string]any{
   530  							"label":       "Label of textarea",
   531  							"description": "Description of textarea",
   532  							"placeholder": "Placeholder of textarea",
   533  							"value":       "Value of textarea",
   534  							"render":      "bash",
   535  						},
   536  						Validations: map[string]any{
   537  							"required": true,
   538  						},
   539  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
   540  					},
   541  					{
   542  						Type: "input",
   543  						ID:   "id3",
   544  						Attributes: map[string]any{
   545  							"label":       "Label of input",
   546  							"description": "Description of input",
   547  							"placeholder": "Placeholder of input",
   548  							"value":       "Value of input",
   549  						},
   550  						Validations: map[string]any{
   551  							"required":  true,
   552  							"is_number": true,
   553  							"regex":     "[a-zA-Z0-9]+",
   554  						},
   555  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
   556  					},
   557  					{
   558  						Type: "dropdown",
   559  						ID:   "id4",
   560  						Attributes: map[string]any{
   561  							"label":       "Label of dropdown",
   562  							"description": "Description of dropdown",
   563  							"multiple":    true,
   564  							"options": []any{
   565  								"Option 1 of dropdown",
   566  								"Option 2 of dropdown",
   567  								"Option 3 of dropdown",
   568  							},
   569  							"default": 1,
   570  						},
   571  						Validations: map[string]any{
   572  							"required": true,
   573  						},
   574  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
   575  					},
   576  					{
   577  						Type: "checkboxes",
   578  						ID:   "id5",
   579  						Attributes: map[string]any{
   580  							"label":       "Label of checkboxes",
   581  							"description": "Description of checkboxes",
   582  							"options": []any{
   583  								map[string]any{"label": "Option 1 of checkboxes", "required": true},
   584  								map[string]any{"label": "Option 2 of checkboxes", "required": false},
   585  								map[string]any{"label": "Hidden Option 3 of checkboxes", "visible": []string{"content"}},
   586  								map[string]any{"label": "Required but not submitted", "required": true, "visible": []string{"form"}},
   587  							},
   588  						},
   589  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
   590  					},
   591  				},
   592  				FileName: "test.yaml",
   593  			},
   594  			wantErr: "",
   595  		},
   596  		{
   597  			name: "single label",
   598  			content: `
   599  name: Name
   600  title: Title
   601  about: About
   602  labels: label1
   603  ref: Ref
   604  body:
   605    - type: markdown
   606      id: id1
   607      attributes:
   608        value: Value of the markdown shown in form
   609    - type: markdown
   610      id: id2
   611      attributes:
   612        value: Value of the markdown shown in created issue
   613      visible: [content]
   614  `,
   615  			want: &api.IssueTemplate{
   616  				Name:   "Name",
   617  				Title:  "Title",
   618  				About:  "About",
   619  				Labels: []string{"label1"},
   620  				Ref:    "Ref",
   621  				Fields: []*api.IssueFormField{
   622  					{
   623  						Type: "markdown",
   624  						ID:   "id1",
   625  						Attributes: map[string]any{
   626  							"value": "Value of the markdown shown in form",
   627  						},
   628  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
   629  					},
   630  					{
   631  						Type: "markdown",
   632  						ID:   "id2",
   633  						Attributes: map[string]any{
   634  							"value": "Value of the markdown shown in created issue",
   635  						},
   636  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleContent},
   637  					},
   638  				},
   639  				FileName: "test.yaml",
   640  			},
   641  			wantErr: "",
   642  		},
   643  		{
   644  			name: "comma-delimited labels",
   645  			content: `
   646  name: Name
   647  title: Title
   648  about: About
   649  labels: label1,label2,,label3 ,,
   650  ref: Ref
   651  body:
   652    - type: markdown
   653      id: id1
   654      attributes:
   655        value: Value of the markdown
   656  `,
   657  			want: &api.IssueTemplate{
   658  				Name:   "Name",
   659  				Title:  "Title",
   660  				About:  "About",
   661  				Labels: []string{"label1", "label2", "label3"},
   662  				Ref:    "Ref",
   663  				Fields: []*api.IssueFormField{
   664  					{
   665  						Type: "markdown",
   666  						ID:   "id1",
   667  						Attributes: map[string]any{
   668  							"value": "Value of the markdown",
   669  						},
   670  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
   671  					},
   672  				},
   673  				FileName: "test.yaml",
   674  			},
   675  			wantErr: "",
   676  		},
   677  		{
   678  			name: "empty string as labels",
   679  			content: `
   680  name: Name
   681  title: Title
   682  about: About
   683  labels: ''
   684  ref: Ref
   685  body:
   686    - type: markdown
   687      id: id1
   688      attributes:
   689        value: Value of the markdown
   690  `,
   691  			want: &api.IssueTemplate{
   692  				Name:   "Name",
   693  				Title:  "Title",
   694  				About:  "About",
   695  				Labels: nil,
   696  				Ref:    "Ref",
   697  				Fields: []*api.IssueFormField{
   698  					{
   699  						Type: "markdown",
   700  						ID:   "id1",
   701  						Attributes: map[string]any{
   702  							"value": "Value of the markdown",
   703  						},
   704  						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
   705  					},
   706  				},
   707  				FileName: "test.yaml",
   708  			},
   709  			wantErr: "",
   710  		},
   711  		{
   712  			name:     "comma delimited labels in markdown",
   713  			filename: "test.md",
   714  			content: `---
   715  name: Name
   716  title: Title
   717  about: About
   718  labels: label1,label2,,label3 ,,
   719  ref: Ref
   720  ---
   721  Content
   722  `,
   723  			want: &api.IssueTemplate{
   724  				Name:     "Name",
   725  				Title:    "Title",
   726  				About:    "About",
   727  				Labels:   []string{"label1", "label2", "label3"},
   728  				Ref:      "Ref",
   729  				Fields:   nil,
   730  				Content:  "Content\n",
   731  				FileName: "test.md",
   732  			},
   733  			wantErr: "",
   734  		},
   735  	}
   736  	for _, tt := range tests {
   737  		t.Run(tt.name, func(t *testing.T) {
   738  			filename := "test.yaml"
   739  			if tt.filename != "" {
   740  				filename = tt.filename
   741  			}
   742  			tmpl, err := unmarshal(filename, []byte(tt.content))
   743  			require.NoError(t, err)
   744  			if tt.wantErr != "" {
   745  				require.EqualError(t, Validate(tmpl), tt.wantErr)
   746  			} else {
   747  				require.NoError(t, Validate(tmpl))
   748  				want, _ := json.Marshal(tt.want)
   749  				got, _ := json.Marshal(tmpl)
   750  				require.JSONEq(t, string(want), string(got))
   751  			}
   752  		})
   753  	}
   754  }
   755  
   756  func TestRenderToMarkdown(t *testing.T) {
   757  	type args struct {
   758  		template string
   759  		values   url.Values
   760  	}
   761  	tests := []struct {
   762  		name string
   763  		args args
   764  		want string
   765  	}{
   766  		{
   767  			name: "normal",
   768  			args: args{
   769  				template: `
   770  name: Name
   771  title: Title
   772  about: About
   773  labels: ["label1", "label2"]
   774  ref: Ref
   775  body:
   776    - type: markdown
   777      id: id1
   778      attributes:
   779        value: Value of the markdown shown in form
   780    - type: markdown
   781      id: id2
   782      attributes:
   783        value: Value of the markdown shown in created issue
   784      visible: [content]
   785    - type: textarea
   786      id: id3
   787      attributes:
   788        label: Label of textarea
   789        description: Description of textarea
   790        placeholder: Placeholder of textarea
   791        value: Value of textarea
   792        render: bash
   793      validations:
   794        required: true
   795    - type: input
   796      id: id4
   797      attributes:
   798        label: Label of input
   799        description: Description of input
   800        placeholder: Placeholder of input
   801        value: Value of input
   802        hide_label: true
   803      validations:
   804        required: true
   805        is_number: true
   806        regex: "[a-zA-Z0-9]+"
   807    - type: dropdown
   808      id: id5
   809      attributes:
   810        label: Label of dropdown
   811        description: Description of dropdown
   812        multiple: true
   813        options:
   814          - Option 1 of dropdown
   815          - Option 2 of dropdown
   816          - Option 3 of dropdown
   817      validations:
   818        required: true
   819    - type: checkboxes
   820      id: id6
   821      attributes:
   822        label: Label of checkboxes
   823        description: Description of checkboxes
   824        options:
   825          - label: Option 1 of checkboxes
   826            required: true
   827          - label: Option 2 of checkboxes
   828            required: false
   829          - label: Option 3 of checkboxes
   830            required: true
   831            visible: [form]
   832          - label: Hidden Option of checkboxes
   833            visible: [content]
   834  `,
   835  				values: map[string][]string{
   836  					"form-field-id3":   {"Value of id3"},
   837  					"form-field-id4":   {"Value of id4"},
   838  					"form-field-id5":   {"0,1"},
   839  					"form-field-id6-0": {"on"},
   840  					"form-field-id6-2": {"on"},
   841  				},
   842  			},
   843  
   844  			want: `Value of the markdown shown in created issue
   845  
   846  ### Label of textarea
   847  
   848  ` + "```bash\nValue of id3\n```" + `
   849  
   850  Value of id4
   851  
   852  ### Label of dropdown
   853  
   854  Option 1 of dropdown, Option 2 of dropdown
   855  
   856  ### Label of checkboxes
   857  
   858  - [x] Option 1 of checkboxes
   859  - [ ] Option 2 of checkboxes
   860  - [ ] Hidden Option of checkboxes
   861  
   862  `,
   863  		},
   864  	}
   865  	for _, tt := range tests {
   866  		t.Run(tt.name, func(t *testing.T) {
   867  			template, err := Unmarshal("test.yaml", []byte(tt.args.template))
   868  			if err != nil {
   869  				t.Fatal(err)
   870  			}
   871  			if got := RenderToMarkdown(template, tt.args.values); got != tt.want {
   872  				assert.EqualValues(t, tt.want, got)
   873  			}
   874  		})
   875  	}
   876  }
   877  
   878  func Test_minQuotes(t *testing.T) {
   879  	type args struct {
   880  		value string
   881  	}
   882  	tests := []struct {
   883  		name string
   884  		args args
   885  		want string
   886  	}{
   887  		{
   888  			name: "without quote",
   889  			args: args{
   890  				value: "Hello\nWorld",
   891  			},
   892  			want: "```",
   893  		},
   894  		{
   895  			name: "with 1 quote",
   896  			args: args{
   897  				value: "Hello\nWorld\n`text`\n",
   898  			},
   899  			want: "```",
   900  		},
   901  		{
   902  			name: "with 3 quotes",
   903  			args: args{
   904  				value: "Hello\nWorld\n`text`\n```go\ntext\n```\n",
   905  			},
   906  			want: "````",
   907  		},
   908  		{
   909  			name: "with more quotes",
   910  			args: args{
   911  				value: "Hello\nWorld\n`text`\n```go\ntext\n```\n``````````bash\ntext\n``````````\n",
   912  			},
   913  			want: "```````````",
   914  		},
   915  		{
   916  			name: "not leading quotes",
   917  			args: args{
   918  				value: "Hello\nWorld`text````go\ntext`````````````bash\ntext``````````\n",
   919  			},
   920  			want: "```",
   921  		},
   922  	}
   923  	for _, tt := range tests {
   924  		t.Run(tt.name, func(t *testing.T) {
   925  			if got := minQuotes(tt.args.value); got != tt.want {
   926  				t.Errorf("minQuotes() = %v, want %v", got, tt.want)
   927  			}
   928  		})
   929  	}
   930  }