mirror of
https://github.com/romdo/go-conventionalcommit.git
synced 2026-02-19 08:06:41 +00:00
wip(parser): partly finished Message parser
This commit is contained in:
54
buffer.go
54
buffer.go
@@ -1,38 +1,5 @@
|
||||
package conventionalcommit
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// footerToken will match against all variations of Conventional Commit footer
|
||||
// formats.
|
||||
//
|
||||
// Examples of valid footer tokens:
|
||||
//
|
||||
// Approved-by: John Carter
|
||||
// ReviewdBy: Noctis
|
||||
// Fixes #49
|
||||
// Reverts #SOL-42
|
||||
// BREAKING CHANGE: Flux capacitor no longer exists.
|
||||
// BREAKING-CHANGE: Time will flow backwads
|
||||
//
|
||||
// Examples of invalid footer tokens:
|
||||
//
|
||||
// Approved-by:
|
||||
// Approved-by:John Carter
|
||||
// Approved by: John Carter
|
||||
// ReviewdBy: Noctis
|
||||
// Fixes#49
|
||||
// Fixes #
|
||||
// Fixes 49
|
||||
// BREAKING CHANGE:Flux capacitor no longer exists.
|
||||
// Breaking Change: Flux capacitor no longer exists.
|
||||
// Breaking-Change: Time will flow backwads
|
||||
//
|
||||
var footerToken = regexp.MustCompile(
|
||||
`^(?:([\w-]+)\s+(#.+)|([\w-]+|BREAKING[\s-]CHANGE):\s+(.+))$`,
|
||||
)
|
||||
|
||||
// Buffer represents a commit message in a more structured form than a simple
|
||||
// string or byte slice. This makes it easier to process a message for the
|
||||
// purposes of extracting detailed information, linting, and formatting.
|
||||
@@ -119,11 +86,11 @@ func NewBuffer(message []byte) *Buffer {
|
||||
lastLen++
|
||||
}
|
||||
|
||||
// If last paragraph starts with a Convention Commit footer token, it is the
|
||||
// foot section, otherwise it is part of the body.
|
||||
// If last paragraph starts with a Conventional Commit footer token, it is
|
||||
// the foot section, otherwise it is part of the body.
|
||||
if lastLen > 0 {
|
||||
line := buf.lines[buf.lastLine-lastLen+1]
|
||||
if footerToken.Match(line.Content) {
|
||||
if FooterToken.Match(line.Content) {
|
||||
buf.footLen = lastLen
|
||||
}
|
||||
}
|
||||
@@ -176,6 +143,15 @@ func (s *Buffer) Lines() Lines {
|
||||
return s.lines[s.firstLine : s.lastLine+1]
|
||||
}
|
||||
|
||||
// LinesRaw returns all lines of the buffer including any blank lines at the
|
||||
// beginning and end of the buffer.
|
||||
func (s *Buffer) LinesRaw() Lines {
|
||||
return s.lines
|
||||
}
|
||||
|
||||
// LineCount returns number of lines in the buffer after discarding blank lines
|
||||
// from the beginning and end of the buffer. Effectively counting all lines from
|
||||
// the first to the last line which contain any non-whitespace characters.
|
||||
func (s *Buffer) LineCount() int {
|
||||
if s.headLen == 0 {
|
||||
return 0
|
||||
@@ -184,6 +160,12 @@ func (s *Buffer) LineCount() int {
|
||||
return (s.lastLine + 1) - s.firstLine
|
||||
}
|
||||
|
||||
// LineCountRaw returns the number of lines in the buffer including any blank
|
||||
// lines at the beginning and end of the buffer.
|
||||
func (s *Buffer) LineCountRaw() int {
|
||||
return len(s.lines)
|
||||
}
|
||||
|
||||
// Bytes renders the Buffer back into a byte slice, without any leading or
|
||||
// trailing whitespace lines. Leading whitespace on the first line which
|
||||
// contains non-whitespace characters is retained. It is only whole lines
|
||||
|
||||
@@ -994,6 +994,55 @@ func BenchmarkBuffer_Lines(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuffer_LinesRaw(t *testing.T) {
|
||||
for _, tt := range bufferTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
want := tt.wantBuffer.lines[0:]
|
||||
|
||||
got := tt.wantBuffer.LinesRaw()
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuffer_LineCount(t *testing.T) {
|
||||
for _, tt := range bufferTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
want := tt.wantLines[1]
|
||||
|
||||
got := tt.wantBuffer.LineCount()
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBuffer_LineCount(b *testing.B) {
|
||||
for _, tt := range bufferTestCases {
|
||||
if tt.bytes == nil {
|
||||
continue
|
||||
}
|
||||
b.Run(tt.name, func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_ = tt.wantBuffer.LineCount()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuffer_LineCountRaw(t *testing.T) {
|
||||
for _, tt := range bufferTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
want := len(tt.wantBuffer.lines)
|
||||
|
||||
got := tt.wantBuffer.LineCountRaw()
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuffer_Bytes(t *testing.T) {
|
||||
for _, tt := range bufferTestCases {
|
||||
if tt.bytes == nil {
|
||||
|
||||
8
line.go
8
line.go
@@ -58,9 +58,9 @@ type Lines []*Line
|
||||
// basis.
|
||||
func NewLines(content []byte) Lines {
|
||||
r := Lines{}
|
||||
cLen := len(content)
|
||||
length := len(content)
|
||||
|
||||
if cLen == 0 {
|
||||
if length == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -68,13 +68,13 @@ func NewLines(content []byte) Lines {
|
||||
var breaks [][]int
|
||||
|
||||
// Locate each line break within content.
|
||||
for i := 0; i < cLen; i++ {
|
||||
for i := 0; i < length; i++ {
|
||||
switch content[i] {
|
||||
case lf:
|
||||
breaks = append(breaks, []int{i, i + 1})
|
||||
case cr:
|
||||
b := []int{i, i + 1}
|
||||
if i+1 < cLen && content[i+1] == lf {
|
||||
if i+1 < length && content[i+1] == lf {
|
||||
b[1]++
|
||||
i++
|
||||
}
|
||||
|
||||
178
message.go
Normal file
178
message.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package conventionalcommit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Err = errors.New("conventionalcommit")
|
||||
ErrEmptyMessage = fmt.Errorf("%w: empty message", Err)
|
||||
)
|
||||
|
||||
// HeaderToken will match a Conventional Commit formatted subject line, to
|
||||
// extract type, scope, breaking change (bool), and description.
|
||||
//
|
||||
// It is intentionally VERY forgiving so as to be able to extract the various
|
||||
// parts even when things aren't quite right.
|
||||
var HeaderToken = regexp.MustCompile(
|
||||
`^([^\(\)\r\n]*?)(\((.*?)\)\s*)?(!)?(\s*\:)\s(.*)$`,
|
||||
)
|
||||
|
||||
// FooterToken will match against all variations of Conventional Commit footer
|
||||
// formats.
|
||||
//
|
||||
// Examples of valid footer tokens:
|
||||
//
|
||||
// Approved-by: John Carter
|
||||
// ReviewdBy: Noctis
|
||||
// Fixes #49
|
||||
// Reverts #SOL-42
|
||||
// BREAKING CHANGE: Flux capacitor no longer exists.
|
||||
// BREAKING-CHANGE: Time will flow backwads
|
||||
//
|
||||
// Examples of invalid footer tokens:
|
||||
//
|
||||
// Approved-by:
|
||||
// Approved-by:John Carter
|
||||
// Approved by: John Carter
|
||||
// ReviewdBy: Noctis
|
||||
// Fixes#49
|
||||
// Fixes #
|
||||
// Fixes 49
|
||||
// BREAKING CHANGE:Flux capacitor no longer exists.
|
||||
// Breaking Change: Flux capacitor no longer exists.
|
||||
// Breaking-Change: Time will flow backwads
|
||||
//
|
||||
var FooterToken = regexp.MustCompile(
|
||||
`^([\w-]+|BREAKING[\s-]CHANGE)(?:\s*(:)\s+|\s+(#))(.+)$`,
|
||||
)
|
||||
|
||||
// Message represents a Conventional Commit message in a structured way.
|
||||
type Message struct {
|
||||
// Type indicates what kind of a change the commit message describes.
|
||||
Type string
|
||||
|
||||
// Scope indicates the context/component/area that the change affects.
|
||||
Scope string
|
||||
|
||||
// Description is the primary description for the commit.
|
||||
Description string
|
||||
|
||||
// Body is the main text body of the commit message. Effectively all text
|
||||
// between the subject line, and any footers if present.
|
||||
Body string
|
||||
|
||||
// Footers are all footers which are not references or breaking changes.
|
||||
Footers []*Footer
|
||||
|
||||
// References are all footers defined with a reference style token, for
|
||||
// example:
|
||||
//
|
||||
// Fixes #42
|
||||
References []*Reference
|
||||
|
||||
// Breaking is set to true if the message subject included the "!" breaking
|
||||
// change indicator.
|
||||
Breaking bool
|
||||
|
||||
// BreakingChanges includes the descriptions from all BREAKING CHANGE
|
||||
// footers.
|
||||
BreakingChanges []string
|
||||
}
|
||||
|
||||
func NewMessage(buf *Buffer) (*Message, error) {
|
||||
msg := &Message{}
|
||||
count := buf.LineCount()
|
||||
|
||||
if count == 0 {
|
||||
return nil, ErrEmptyMessage
|
||||
}
|
||||
|
||||
msg.Description = buf.Head().Join("\n")
|
||||
if m := HeaderToken.FindStringSubmatch(msg.Description); len(m) > 0 {
|
||||
msg.Type = strings.TrimSpace(m[1])
|
||||
msg.Scope = strings.TrimSpace(m[3])
|
||||
msg.Breaking = m[4] == "!"
|
||||
msg.Description = m[6]
|
||||
}
|
||||
|
||||
msg.Body = buf.Body().Join("\n")
|
||||
|
||||
if foot := buf.Foot(); len(foot) > 0 {
|
||||
footers := parseFooters(foot)
|
||||
|
||||
for _, f := range footers {
|
||||
name := string(f.name)
|
||||
value := string(f.value)
|
||||
|
||||
switch {
|
||||
case f.ref:
|
||||
msg.References = append(msg.References, &Reference{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
case name == "BREAKING CHANGE" || name == "BREAKING-CHANGE":
|
||||
msg.BreakingChanges = append(msg.BreakingChanges, value)
|
||||
default:
|
||||
msg.Footers = append(msg.Footers, &Footer{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (s *Message) IsBreakingChange() bool {
|
||||
return s.Breaking || len(s.BreakingChanges) > 0
|
||||
}
|
||||
|
||||
func parseFooters(lines Lines) []*rawFooter {
|
||||
var footers []*rawFooter
|
||||
footer := &rawFooter{}
|
||||
for _, line := range lines {
|
||||
if m := FooterToken.FindSubmatch(line.Content); m != nil {
|
||||
if len(footer.name) > 0 {
|
||||
footers = append(footers, footer)
|
||||
}
|
||||
|
||||
footer = &rawFooter{}
|
||||
if len(m[3]) > 0 {
|
||||
footer.ref = true
|
||||
footer.value = []byte{hash}
|
||||
}
|
||||
footer.name = m[1]
|
||||
footer.value = append(footer.value, m[4]...)
|
||||
} else if len(footer.name) > 0 {
|
||||
footer.value = append(footer.value, lf)
|
||||
footer.value = append(footer.value, line.Content...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(footer.name) > 0 {
|
||||
footers = append(footers, footer)
|
||||
}
|
||||
|
||||
return footers
|
||||
}
|
||||
|
||||
type rawFooter struct {
|
||||
name []byte
|
||||
value []byte
|
||||
ref bool
|
||||
}
|
||||
|
||||
type Footer struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
80
message_test.go
Normal file
80
message_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package conventionalcommit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMessage_IsBreakingChange(t *testing.T) {
|
||||
type fields struct {
|
||||
Breaking bool
|
||||
BreakingChanges []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "false breaking flag, no change texts",
|
||||
fields: fields{
|
||||
Breaking: false,
|
||||
BreakingChanges: []string{},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "true breaking flag, no change texts",
|
||||
fields: fields{
|
||||
Breaking: true,
|
||||
BreakingChanges: []string{},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "false breaking flag, 1 change texts",
|
||||
fields: fields{
|
||||
Breaking: false,
|
||||
BreakingChanges: []string{"be careful"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "true breaking flag, 1 change texts",
|
||||
fields: fields{
|
||||
Breaking: true,
|
||||
BreakingChanges: []string{"be careful"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "false breaking flag, 3 change texts",
|
||||
fields: fields{
|
||||
Breaking: false,
|
||||
BreakingChanges: []string{"be careful", "oops", "ouch"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "true breaking flag, 3 change texts",
|
||||
fields: fields{
|
||||
Breaking: true,
|
||||
BreakingChanges: []string{"be careful", "oops", "ouch"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
msg := &Message{
|
||||
Breaking: tt.fields.Breaking,
|
||||
BreakingChanges: tt.fields.BreakingChanges,
|
||||
}
|
||||
|
||||
got := msg.IsBreakingChange()
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
9
parse.go
Normal file
9
parse.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package conventionalcommit
|
||||
|
||||
// Parse parses a conventional commit message and returns it as a *Message
|
||||
// struct.
|
||||
func Parse(message []byte) (*Message, error) {
|
||||
buffer := NewBuffer(message)
|
||||
|
||||
return NewMessage(buffer)
|
||||
}
|
||||
421
parse_test.go
Normal file
421
parse_test.go
Normal file
@@ -0,0 +1,421 @@
|
||||
package conventionalcommit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
message []byte
|
||||
want *Message
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
message: []byte{},
|
||||
wantErr: "conventionalcommit: empty message",
|
||||
},
|
||||
{
|
||||
name: "description only",
|
||||
message: []byte("change a thing"),
|
||||
want: &Message{
|
||||
Description: "change a thing",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "description and body",
|
||||
message: []byte(`change a thing
|
||||
|
||||
more stuff
|
||||
and more`,
|
||||
),
|
||||
want: &Message{
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type and description",
|
||||
message: []byte("feat: change a thing"),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type, description and body",
|
||||
message: []byte(
|
||||
"feat: change a thing\n\nmore stuff\nand more",
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type, scope and description",
|
||||
message: []byte("feat(token): change a thing"),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type, scope, description and body",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
more stuff
|
||||
and more`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "breaking change in subject line",
|
||||
message: []byte(
|
||||
`feat!: change a thing
|
||||
|
||||
more stuff
|
||||
and more`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
Breaking: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "breaking change in subject line with scope",
|
||||
message: []byte(
|
||||
`feat(token)!: change a thing
|
||||
|
||||
more stuff
|
||||
and more`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
Breaking: true,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "BREAKING CHANGE footer",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
BREAKING CHANGE: will blow up
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
BreakingChanges: []string{"will blow up"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BREAKING-CHANGE footer",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
BREAKING-CHANGE: maybe not
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
BreakingChanges: []string{"maybe not"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reference footer",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
Fixes #349
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
References: []*Reference{
|
||||
{Name: "Fixes", Value: "#349"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reference (alt) footer",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
Reverts #SOL-934
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
References: []*Reference{
|
||||
{Name: "Reverts", Value: "#SOL-934"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token footer",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
Approved-by: John Carter
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Footers: []*Footer{
|
||||
{Name: "Approved-by", Value: "John Carter"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token (alt) footer",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
ReviewedBy: Noctis
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Footers: []*Footer{
|
||||
{Name: "ReviewedBy", Value: "Noctis"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "BREAKING CHANGE footer with body",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
BREAKING CHANGE: will blow up
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
BreakingChanges: []string{"will blow up"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BREAKING-CHANGE footer with body",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
BREAKING-CHANGE: maybe not
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
BreakingChanges: []string{"maybe not"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reference footer with body",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
Fixes #349
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
References: []*Reference{
|
||||
{Name: "Fixes", Value: "#349"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reference (alt) footer with body",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
Reverts #SOL-934
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
References: []*Reference{
|
||||
{Name: "Reverts", Value: "#SOL-934"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token footer with body",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
Approved-by: John Carter
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
Footers: []*Footer{
|
||||
{Name: "Approved-by", Value: "John Carter"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token (alt) footer with body",
|
||||
message: []byte(
|
||||
`feat: change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
ReviewedBy: Noctis
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
Footers: []*Footer{
|
||||
{Name: "ReviewedBy", Value: "Noctis"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type, scope, description, body and footers",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
more stuff
|
||||
and more
|
||||
|
||||
BREAKING CHANGE: will blow up
|
||||
BREAKING-CHANGE: maybe not
|
||||
Fixes #349
|
||||
Reverts #SOL-934
|
||||
Approved-by: John Carter
|
||||
ReviewedBy: Noctis
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "more stuff\nand more",
|
||||
Footers: []*Footer{
|
||||
{Name: "Approved-by", Value: "John Carter"},
|
||||
{Name: "ReviewedBy", Value: "Noctis"},
|
||||
},
|
||||
References: []*Reference{
|
||||
{Name: "Fixes", Value: "#349"},
|
||||
{Name: "Reverts", Value: "#SOL-934"},
|
||||
},
|
||||
BreakingChanges: []string{"will blow up", "maybe not"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-line footers",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
Some stuff
|
||||
|
||||
BREAKING CHANGE: Nam euismod tellus id erat. Cum sociis natoque penatibus
|
||||
et magnis dis parturient montes, nascetur ridiculous mus.
|
||||
Approved-by: John Carter
|
||||
and Noctis
|
||||
Fixes #SOL-349 and also
|
||||
#SOL-9440
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "Some stuff",
|
||||
Footers: []*Footer{
|
||||
{Name: "Approved-by", Value: "John Carter\nand Noctis"},
|
||||
},
|
||||
References: []*Reference{
|
||||
{Name: "Fixes", Value: "#SOL-349 and also\n#SOL-9440"},
|
||||
},
|
||||
BreakingChanges: []string{
|
||||
`Nam euismod tellus id erat. Cum sociis natoque penatibus
|
||||
et magnis dis parturient montes, nascetur ridiculous mus.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "indented footer",
|
||||
message: []byte(
|
||||
`feat(token): change a thing
|
||||
|
||||
Some stuff
|
||||
|
||||
Approved-by: John Carter
|
||||
`,
|
||||
),
|
||||
want: &Message{
|
||||
Type: "feat",
|
||||
Scope: "token",
|
||||
Description: "change a thing",
|
||||
Body: "Some stuff\n\n Approved-by: John Carter",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Parse(tt.message)
|
||||
|
||||
if tt.wantErr != "" {
|
||||
assert.EqualError(t, err, tt.wantErr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user