mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-25 17:44:32 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			965 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			965 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2022 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package template
 | |
| 
 | |
| import (
 | |
| 	"net/url"
 | |
| 	"testing"
 | |
| 
 | |
| 	"code.gitea.io/gitea/modules/json"
 | |
| 	api "code.gitea.io/gitea/modules/structs"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| func TestValidate(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name     string
 | |
| 		filename string
 | |
| 		content  string
 | |
| 		want     *api.IssueTemplate
 | |
| 		wantErr  string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:    "miss name",
 | |
| 			content: ``,
 | |
| 			wantErr: "'name' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "miss about",
 | |
| 			content: `
 | |
| name: "test"
 | |
| `,
 | |
| 			wantErr: "'about' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "miss body",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| `,
 | |
| 			wantErr: "'body' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "markdown miss value",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "markdown"
 | |
| `,
 | |
| 			wantErr: "body[0](markdown): 'value' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "markdown invalid value",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "markdown"
 | |
|     attributes:
 | |
|       value: true
 | |
| `,
 | |
| 			wantErr: "body[0](markdown): 'value' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "markdown empty value",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "markdown"
 | |
|     attributes:
 | |
|       value: ""
 | |
| `,
 | |
| 			wantErr: "body[0](markdown): 'value' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "textarea invalid id",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "textarea"
 | |
|     id: "?"
 | |
| `,
 | |
| 			wantErr: "body[0](textarea): 'id' should contain only alphanumeric, '-' and '_'",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "textarea miss label",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "textarea"
 | |
|     id: "1"
 | |
| `,
 | |
| 			wantErr: "body[0](textarea): 'label' is required",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "textarea conflict id",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "textarea"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|   - type: "textarea"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "b"
 | |
| `,
 | |
| 			wantErr: "body[1](textarea): 'id' should be unique",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "textarea invalid description",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "textarea"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       description: true
 | |
| `,
 | |
| 			wantErr: "body[0](textarea): 'description' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "textarea invalid required",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "textarea"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|     validations:
 | |
|       required: "on"
 | |
| `,
 | |
| 			wantErr: "body[0](textarea): 'required' should be a bool",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "input invalid description",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "input"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       description: true
 | |
| `,
 | |
| 			wantErr: "body[0](input): 'description' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "input invalid is_number",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "input"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|     validations:
 | |
|       is_number: "yes"
 | |
| `,
 | |
| 			wantErr: "body[0](input): 'is_number' should be a bool",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "input invalid regex",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "input"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|     validations:
 | |
|       regex: true
 | |
| `,
 | |
| 			wantErr: "body[0](input): 'regex' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown invalid description",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "dropdown"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       description: true
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): 'description' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown invalid multiple",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "dropdown"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       multiple: "on"
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): 'multiple' should be a bool",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown invalid list",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "dropdown"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       list: "on"
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): 'list' should be a bool",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "checkboxes invalid description",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "checkboxes"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       description: true
 | |
| `,
 | |
| 			wantErr: "body[0](checkboxes): 'description' should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "invalid type",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "video"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
| `,
 | |
| 			wantErr: "body[0](video): unknown type",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown miss options",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "dropdown"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): 'options' is required and should be a array",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown invalid options",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "dropdown"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       options:
 | |
|         - "a"
 | |
|         - true
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown), option[1]: should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "checkboxes invalid options",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "checkboxes"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       options:
 | |
|         - "a"
 | |
|         - true
 | |
| `,
 | |
| 			wantErr: "body[0](checkboxes), option[0]: should be a dictionary",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "checkboxes option miss label",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "checkboxes"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       options:
 | |
|         - required: true
 | |
| `,
 | |
| 			wantErr: "body[0](checkboxes), option[0]: 'label' is required and should be a string",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "checkboxes option invalid required",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "checkboxes"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|       options:
 | |
|         - label: "a"
 | |
|           required: "on"
 | |
| `,
 | |
| 			wantErr: "body[0](checkboxes), option[0]: 'required' should be a bool",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "field is required but hidden",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: "input"
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: "a"
 | |
|     validations:
 | |
|       required: true
 | |
|     visible: [content]
 | |
| `,
 | |
| 			wantErr: "body[0](input): can not require a hidden field",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "checkboxes is required but hidden",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: checkboxes
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: Label of checkboxes
 | |
|       description: Description of checkboxes
 | |
|       options:
 | |
|         - label: Option 1
 | |
|           required: false
 | |
|         - label: Required and hidden
 | |
|           required: true
 | |
|           visible: [content]
 | |
| `,
 | |
| 			wantErr: "body[0](checkboxes), option[1]: can not require a hidden checkbox",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown default is not an integer",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: dropdown
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: Label of dropdown
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|       default: "def"
 | |
|     validations:
 | |
|       required: true
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): 'default' should be an int",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown default is out of range",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: dropdown
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: Label of dropdown
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|       default: 3
 | |
|     validations:
 | |
|       required: true
 | |
| `,
 | |
| 			wantErr: "body[0](dropdown): the value of 'default' is out of range",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dropdown without default is valid",
 | |
| 			content: `
 | |
| name: "test"
 | |
| about: "this is about"
 | |
| body:
 | |
|   - type: dropdown
 | |
|     id: "1"
 | |
|     attributes:
 | |
|       label: Label of dropdown
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|     validations:
 | |
|       required: true
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:  "test",
 | |
| 				About: "this is about",
 | |
| 				Fields: []*api.IssueFormField{
 | |
| 					{
 | |
| 						Type: "dropdown",
 | |
| 						ID:   "1",
 | |
| 						Attributes: map[string]any{
 | |
| 							"label":       "Label of dropdown",
 | |
| 							"description": "Description of dropdown",
 | |
| 							"multiple":    true,
 | |
| 							"options": []any{
 | |
| 								"Option 1 of dropdown",
 | |
| 								"Option 2 of dropdown",
 | |
| 								"Option 3 of dropdown",
 | |
| 							},
 | |
| 						},
 | |
| 						Validations: map[string]any{
 | |
| 							"required": true,
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 				},
 | |
| 				FileName: "test.yaml",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "valid",
 | |
| 			content: `
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: ["label1", "label2"]
 | |
| assignees: ["user1", "user2"]
 | |
| ref: Ref
 | |
| body:
 | |
|   - type: markdown
 | |
|     id: id1
 | |
|     attributes:
 | |
|       value: Value of the markdown
 | |
|   - type: textarea
 | |
|     id: id2
 | |
|     attributes:
 | |
|       label: Label of textarea
 | |
|       description: Description of textarea
 | |
|       placeholder: Placeholder of textarea
 | |
|       value: Value of textarea
 | |
|       render: bash
 | |
|     validations:
 | |
|       required: true
 | |
|   - type: input
 | |
|     id: id3
 | |
|     attributes:
 | |
|       label: Label of input
 | |
|       description: Description of input
 | |
|       placeholder: Placeholder of input
 | |
|       value: Value of input
 | |
|     validations:
 | |
|       required: true
 | |
|       is_number: true
 | |
|       regex: "[a-zA-Z0-9]+"
 | |
|   - type: dropdown
 | |
|     id: id4
 | |
|     attributes:
 | |
|       label: Label of dropdown
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|       default: 1
 | |
|     validations:
 | |
|       required: true
 | |
|   - type: checkboxes
 | |
|     id: id5
 | |
|     attributes:
 | |
|       label: Label of checkboxes
 | |
|       description: Description of checkboxes
 | |
|       options:
 | |
|         - label: Option 1 of checkboxes
 | |
|           required: true
 | |
|         - label: Option 2 of checkboxes
 | |
|           required: false
 | |
|         - label: Hidden Option 3 of checkboxes
 | |
|           visible: [content]
 | |
|         - label: Required but not submitted
 | |
|           required: true
 | |
|           visible: [form]
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:      "Name",
 | |
| 				Title:     "Title",
 | |
| 				About:     "About",
 | |
| 				Labels:    []string{"label1", "label2"},
 | |
| 				Assignees: []string{"user1", "user2"},
 | |
| 				Ref:       "Ref",
 | |
| 				Fields: []*api.IssueFormField{
 | |
| 					{
 | |
| 						Type: "markdown",
 | |
| 						ID:   "id1",
 | |
| 						Attributes: map[string]any{
 | |
| 							"value": "Value of the markdown",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
 | |
| 					},
 | |
| 					{
 | |
| 						Type: "textarea",
 | |
| 						ID:   "id2",
 | |
| 						Attributes: map[string]any{
 | |
| 							"label":       "Label of textarea",
 | |
| 							"description": "Description of textarea",
 | |
| 							"placeholder": "Placeholder of textarea",
 | |
| 							"value":       "Value of textarea",
 | |
| 							"render":      "bash",
 | |
| 						},
 | |
| 						Validations: map[string]any{
 | |
| 							"required": true,
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 					{
 | |
| 						Type: "input",
 | |
| 						ID:   "id3",
 | |
| 						Attributes: map[string]any{
 | |
| 							"label":       "Label of input",
 | |
| 							"description": "Description of input",
 | |
| 							"placeholder": "Placeholder of input",
 | |
| 							"value":       "Value of input",
 | |
| 						},
 | |
| 						Validations: map[string]any{
 | |
| 							"required":  true,
 | |
| 							"is_number": true,
 | |
| 							"regex":     "[a-zA-Z0-9]+",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 					{
 | |
| 						Type: "dropdown",
 | |
| 						ID:   "id4",
 | |
| 						Attributes: map[string]any{
 | |
| 							"label":       "Label of dropdown",
 | |
| 							"description": "Description of dropdown",
 | |
| 							"multiple":    true,
 | |
| 							"options": []any{
 | |
| 								"Option 1 of dropdown",
 | |
| 								"Option 2 of dropdown",
 | |
| 								"Option 3 of dropdown",
 | |
| 							},
 | |
| 							"default": 1,
 | |
| 						},
 | |
| 						Validations: map[string]any{
 | |
| 							"required": true,
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 					{
 | |
| 						Type: "checkboxes",
 | |
| 						ID:   "id5",
 | |
| 						Attributes: map[string]any{
 | |
| 							"label":       "Label of checkboxes",
 | |
| 							"description": "Description of checkboxes",
 | |
| 							"options": []any{
 | |
| 								map[string]any{"label": "Option 1 of checkboxes", "required": true},
 | |
| 								map[string]any{"label": "Option 2 of checkboxes", "required": false},
 | |
| 								map[string]any{"label": "Hidden Option 3 of checkboxes", "visible": []string{"content"}},
 | |
| 								map[string]any{"label": "Required but not submitted", "required": true, "visible": []string{"form"}},
 | |
| 							},
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 				},
 | |
| 				FileName: "test.yaml",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "single label",
 | |
| 			content: `
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: label1
 | |
| ref: Ref
 | |
| body:
 | |
|   - type: markdown
 | |
|     id: id1
 | |
|     attributes:
 | |
|       value: Value of the markdown shown in form
 | |
|   - type: markdown
 | |
|     id: id2
 | |
|     attributes:
 | |
|       value: Value of the markdown shown in created issue
 | |
|     visible: [content]
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:   "Name",
 | |
| 				Title:  "Title",
 | |
| 				About:  "About",
 | |
| 				Labels: []string{"label1"},
 | |
| 				Ref:    "Ref",
 | |
| 				Fields: []*api.IssueFormField{
 | |
| 					{
 | |
| 						Type: "markdown",
 | |
| 						ID:   "id1",
 | |
| 						Attributes: map[string]any{
 | |
| 							"value": "Value of the markdown shown in form",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
 | |
| 					},
 | |
| 					{
 | |
| 						Type: "markdown",
 | |
| 						ID:   "id2",
 | |
| 						Attributes: map[string]any{
 | |
| 							"value": "Value of the markdown shown in created issue",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleContent},
 | |
| 					},
 | |
| 				},
 | |
| 				FileName: "test.yaml",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "comma-delimited labels",
 | |
| 			content: `
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: label1,label2,,label3 ,,
 | |
| ref: Ref
 | |
| body:
 | |
|   - type: markdown
 | |
|     id: id1
 | |
|     attributes:
 | |
|       value: Value of the markdown
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:   "Name",
 | |
| 				Title:  "Title",
 | |
| 				About:  "About",
 | |
| 				Labels: []string{"label1", "label2", "label3"},
 | |
| 				Ref:    "Ref",
 | |
| 				Fields: []*api.IssueFormField{
 | |
| 					{
 | |
| 						Type: "markdown",
 | |
| 						ID:   "id1",
 | |
| 						Attributes: map[string]any{
 | |
| 							"value": "Value of the markdown",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
 | |
| 					},
 | |
| 				},
 | |
| 				FileName: "test.yaml",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "empty string as labels",
 | |
| 			content: `
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: ''
 | |
| ref: Ref
 | |
| body:
 | |
|   - type: markdown
 | |
|     id: id1
 | |
|     attributes:
 | |
|       value: Value of the markdown
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:   "Name",
 | |
| 				Title:  "Title",
 | |
| 				About:  "About",
 | |
| 				Labels: nil,
 | |
| 				Ref:    "Ref",
 | |
| 				Fields: []*api.IssueFormField{
 | |
| 					{
 | |
| 						Type: "markdown",
 | |
| 						ID:   "id1",
 | |
| 						Attributes: map[string]any{
 | |
| 							"value": "Value of the markdown",
 | |
| 						},
 | |
| 						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm},
 | |
| 					},
 | |
| 				},
 | |
| 				FileName: "test.yaml",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name:     "comma delimited labels in markdown",
 | |
| 			filename: "test.md",
 | |
| 			content: `---
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: label1,label2,,label3 ,,
 | |
| ref: Ref
 | |
| ---
 | |
| Content
 | |
| `,
 | |
| 			want: &api.IssueTemplate{
 | |
| 				Name:     "Name",
 | |
| 				Title:    "Title",
 | |
| 				About:    "About",
 | |
| 				Labels:   []string{"label1", "label2", "label3"},
 | |
| 				Ref:      "Ref",
 | |
| 				Fields:   nil,
 | |
| 				Content:  "Content\n",
 | |
| 				FileName: "test.md",
 | |
| 			},
 | |
| 			wantErr: "",
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			filename := "test.yaml"
 | |
| 			if tt.filename != "" {
 | |
| 				filename = tt.filename
 | |
| 			}
 | |
| 			tmpl, err := unmarshal(filename, []byte(tt.content))
 | |
| 			require.NoError(t, err)
 | |
| 			if tt.wantErr != "" {
 | |
| 				require.EqualError(t, Validate(tmpl), tt.wantErr)
 | |
| 			} else {
 | |
| 				require.NoError(t, Validate(tmpl))
 | |
| 				want, _ := json.Marshal(tt.want)
 | |
| 				got, _ := json.Marshal(tmpl)
 | |
| 				require.JSONEq(t, string(want), string(got))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRenderToMarkdown(t *testing.T) {
 | |
| 	type args struct {
 | |
| 		template string
 | |
| 		values   url.Values
 | |
| 	}
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		args args
 | |
| 		want string
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "normal",
 | |
| 			args: args{
 | |
| 				template: `
 | |
| name: Name
 | |
| title: Title
 | |
| about: About
 | |
| labels: ["label1", "label2"]
 | |
| ref: Ref
 | |
| body:
 | |
|   - type: markdown
 | |
|     id: id1
 | |
|     attributes:
 | |
|       value: Value of the markdown shown in form
 | |
|   - type: markdown
 | |
|     id: id2
 | |
|     attributes:
 | |
|       value: Value of the markdown shown in created issue
 | |
|     visible: [content]
 | |
|   - type: textarea
 | |
|     id: id3
 | |
|     attributes:
 | |
|       label: Label of textarea
 | |
|       description: Description of textarea
 | |
|       placeholder: Placeholder of textarea
 | |
|       value: Value of textarea
 | |
|       render: bash
 | |
|     validations:
 | |
|       required: true
 | |
|   - type: input
 | |
|     id: id4
 | |
|     attributes:
 | |
|       label: Label of input
 | |
|       description: Description of input
 | |
|       placeholder: Placeholder of input
 | |
|       value: Value of input
 | |
|       hide_label: true
 | |
|     validations:
 | |
|       required: true
 | |
|       is_number: true
 | |
|       regex: "[a-zA-Z0-9]+"
 | |
|   - type: dropdown
 | |
|     id: id5
 | |
|     attributes:
 | |
|       label: Label of dropdown (one line)
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|     validations:
 | |
|       required: true
 | |
|   - type: dropdown
 | |
|     id: id6
 | |
|     attributes:
 | |
|       label: Label of dropdown (list)
 | |
|       description: Description of dropdown
 | |
|       multiple: true
 | |
|       list: true
 | |
|       options:
 | |
|         - Option 1 of dropdown
 | |
|         - Option 2 of dropdown
 | |
|         - Option 3 of dropdown
 | |
|     validations:
 | |
|       required: true
 | |
|   - type: checkboxes
 | |
|     id: id7
 | |
|     attributes:
 | |
|       label: Label of checkboxes
 | |
|       description: Description of checkboxes
 | |
|       options:
 | |
|         - label: Option 1 of checkboxes
 | |
|           required: true
 | |
|         - label: Option 2 of checkboxes
 | |
|           required: false
 | |
|         - label: Option 3 of checkboxes
 | |
|           required: true
 | |
|           visible: [form]
 | |
|         - label: Hidden Option of checkboxes
 | |
|           visible: [content]
 | |
| `,
 | |
| 				values: map[string][]string{
 | |
| 					"form-field-id3":   {"Value of id3"},
 | |
| 					"form-field-id4":   {"Value of id4"},
 | |
| 					"form-field-id5":   {"0,1"},
 | |
| 					"form-field-id6":   {"1,2"},
 | |
| 					"form-field-id7-0": {"on"},
 | |
| 					"form-field-id7-2": {"on"},
 | |
| 				},
 | |
| 			},
 | |
| 
 | |
| 			want: `Value of the markdown shown in created issue
 | |
| 
 | |
| ### Label of textarea
 | |
| 
 | |
| ` + "```bash\nValue of id3\n```" + `
 | |
| 
 | |
| Value of id4
 | |
| 
 | |
| ### Label of dropdown (one line)
 | |
| 
 | |
| Option 1 of dropdown, Option 2 of dropdown
 | |
| 
 | |
| ### Label of dropdown (list)
 | |
| 
 | |
| - Option 2 of dropdown
 | |
| - Option 3 of dropdown
 | |
| 
 | |
| ### Label of checkboxes
 | |
| 
 | |
| - [x] Option 1 of checkboxes
 | |
| - [ ] Option 2 of checkboxes
 | |
| - [ ] Hidden Option of checkboxes
 | |
| 
 | |
| `,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			template, err := Unmarshal("test.yaml", []byte(tt.args.template))
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 			if got := RenderToMarkdown(template, tt.args.values); got != tt.want {
 | |
| 				assert.Equal(t, tt.want, got)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_minQuotes(t *testing.T) {
 | |
| 	type args struct {
 | |
| 		value string
 | |
| 	}
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		args args
 | |
| 		want string
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "without quote",
 | |
| 			args: args{
 | |
| 				value: "Hello\nWorld",
 | |
| 			},
 | |
| 			want: "```",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "with 1 quote",
 | |
| 			args: args{
 | |
| 				value: "Hello\nWorld\n`text`\n",
 | |
| 			},
 | |
| 			want: "```",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "with 3 quotes",
 | |
| 			args: args{
 | |
| 				value: "Hello\nWorld\n`text`\n```go\ntext\n```\n",
 | |
| 			},
 | |
| 			want: "````",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "with more quotes",
 | |
| 			args: args{
 | |
| 				value: "Hello\nWorld\n`text`\n```go\ntext\n```\n``````````bash\ntext\n``````````\n",
 | |
| 			},
 | |
| 			want: "```````````",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "not leading quotes",
 | |
| 			args: args{
 | |
| 				value: "Hello\nWorld`text````go\ntext`````````````bash\ntext``````````\n",
 | |
| 			},
 | |
| 			want: "```",
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			got := minQuotes(tt.args.value)
 | |
| 			assert.Equal(t, tt.want, got)
 | |
| 		})
 | |
| 	}
 | |
| }
 |