diff options
Diffstat (limited to 'modules/issue/template/template_test.go')
-rw-r--r-- | modules/issue/template/template_test.go | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go new file mode 100644 index 0000000000..883e1e0780 --- /dev/null +++ b/modules/issue/template/template_test.go @@ -0,0 +1,645 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package template + +import ( + "net/url" + "reflect" + "testing" + + "code.gitea.io/gitea/modules/json" + api "code.gitea.io/gitea/modules/structs" +) + +func TestValidate(t *testing.T) { + tests := []struct { + name string + content string + 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: "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", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpl, err := unmarshal("test.yaml", []byte(tt.content)) + if err != nil { + t.Fatal(err) + } + if err := Validate(tmpl); (err == nil) != (tt.wantErr == "") || err != nil && err.Error() != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %q", err, tt.wantErr) + } + }) + } + + t.Run("valid", func(t *testing.T) { + content := ` +name: Name +title: Title +about: About +labels: ["label1", "label2"] +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 + 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: Option 3 of checkboxes + required: true +` + want := &api.IssueTemplate{ + Name: "Name", + Title: "Title", + About: "About", + Labels: []string{"label1", "label2"}, + Ref: "Ref", + Fields: []*api.IssueFormField{ + { + Type: "markdown", + ID: "id1", + Attributes: map[string]interface{}{ + "value": "Value of the markdown", + }, + }, + { + Type: "textarea", + ID: "id2", + Attributes: map[string]interface{}{ + "label": "Label of textarea", + "description": "Description of textarea", + "placeholder": "Placeholder of textarea", + "value": "Value of textarea", + "render": "bash", + }, + Validations: map[string]interface{}{ + "required": true, + }, + }, + { + Type: "input", + ID: "id3", + Attributes: map[string]interface{}{ + "label": "Label of input", + "description": "Description of input", + "placeholder": "Placeholder of input", + "value": "Value of input", + }, + Validations: map[string]interface{}{ + "required": true, + "is_number": true, + "regex": "[a-zA-Z0-9]+", + }, + }, + { + Type: "dropdown", + ID: "id4", + Attributes: map[string]interface{}{ + "label": "Label of dropdown", + "description": "Description of dropdown", + "multiple": true, + "options": []interface{}{ + "Option 1 of dropdown", + "Option 2 of dropdown", + "Option 3 of dropdown", + }, + }, + Validations: map[string]interface{}{ + "required": true, + }, + }, + { + Type: "checkboxes", + ID: "id5", + Attributes: map[string]interface{}{ + "label": "Label of checkboxes", + "description": "Description of checkboxes", + "options": []interface{}{ + map[interface{}]interface{}{"label": "Option 1 of checkboxes", "required": true}, + map[interface{}]interface{}{"label": "Option 2 of checkboxes", "required": false}, + map[interface{}]interface{}{"label": "Option 3 of checkboxes", "required": true}, + }, + }, + }, + }, + FileName: "test.yaml", + } + got, err := unmarshal("test.yaml", []byte(content)) + if err != nil { + t.Fatal(err) + } + if err := Validate(got); err != nil { + t.Errorf("Validate() error = %v", err) + } + if !reflect.DeepEqual(want, got) { + jsonWant, _ := json.Marshal(want) + jsonGot, _ := json.Marshal(got) + t.Errorf("want:\n%s\ngot:\n%s", jsonWant, jsonGot) + } + }) +} + +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 + - 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 + 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: Option 3 of checkboxes + required: true +`, + values: map[string][]string{ + "form-field-id2": {"Value of id2"}, + "form-field-id3": {"Value of id3"}, + "form-field-id4": {"0,1"}, + "form-field-id5-0": {"on"}, + "form-field-id5-2": {"on"}, + }, + }, + want: `### Label of textarea + +` + "```bash\nValue of id2\n```" + ` + +### Label of input + +Value of id3 + +### Label of dropdown + +Option 1 of dropdown, Option 2 of dropdown + +### Label of checkboxes + +- [x] Option 1 of checkboxes +- [ ] Option 2 of checkboxes +- [x] Option 3 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 { + t.Errorf("RenderToMarkdown() = %v, want %v", got, tt.want) + } + }) + } +} + +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) { + if got := minQuotes(tt.args.value); got != tt.want { + t.Errorf("minQuotes() = %v, want %v", got, tt.want) + } + }) + } +} |