Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add basic renderer tests #191

Merged
merged 1 commit into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cli/cmd/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ func printDocs(format string, services []string) cli.Result {
case "console":
renderer = f.ConsoleTreeRenderer{WithValues: false}
case "markdown":
renderer = f.MarkdownTreeRenderer{HeaderPrefix: "### "}
renderer = f.MarkdownTreeRenderer{
HeaderPrefix: "### ",
PropsDescription: "Props can be either supplied using the params argument, or through the URL using \n`?key=value&key=value` etc.\n",
}
default:
return cli.InvalidUsage("invalid format")
}
Expand Down
11 changes: 8 additions & 3 deletions pkg/format/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ func getNode(fieldVal r.Value, fieldInfo *FieldInfo) Node {
}
}

func getRootNode(config types.ServiceConfig) *ContainerNode {
structValue := r.ValueOf(config)
func getRootNode(v interface{}) *ContainerNode {
structValue := r.ValueOf(v)
if structValue.Kind() == r.Ptr {
structValue = structValue.Elem()
}
Expand All @@ -186,7 +186,12 @@ func getRootNode(config types.ServiceConfig) *ContainerNode {
Type: structValue.Type(),
}

infoFields := getStructFieldInfo(fieldInfo.Type, config.Enums())
enums := map[string]types.EnumFormatter{}
if enummer, isEnummer := v.(types.Enummer); isEnummer {
enums = enummer.Enums()
}

infoFields := getStructFieldInfo(fieldInfo.Type, enums)

numFields := len(infoFields)
nodeItems := make([]Node, numFields)
Expand Down
56 changes: 56 additions & 0 deletions pkg/format/render_console_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package format

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
gformat "github.com/onsi/gomega/format"
)

var _ = Describe("RenderConsole", func() {
gformat.CharactersAroundMismatchToInclude = 30
renderer := ConsoleTreeRenderer{WithValues: false}

It("should render the expected output based on config reflection/tags", func() {
actual := testRenderTee(renderer, &struct {
Name string `default:"notempty"`
Host string `url:"host"`
}{})

expected := `
Host string <URL: Host> <Required>
Name string <Default: notempty>
`[1:]
println()
println(actual)

Expect(actual).To(Equal(expected))
})

It("should render url paths in sorted order", func() {
actual := testRenderTee(renderer, &struct {
Host string `url:"host"`
Path1 string `url:"path1"`
Path3 string `url:"path3"`
Path2 string `url:"path2"`
}{})

expected := `
Host string <URL: Host> <Required>
Path1 string <URL: Path> <Required>
Path2 string <URL: Path> <Required>
Path3 string <URL: Path> <Required>
`[1:]

println()
println(actual)

Expect(actual).To(Equal(expected))
})
})

/*

* __TestEnum__
Default: `+"`None`"+`
Possible values: `+"`None`, `Foo`, `Bar`"+`
*/
43 changes: 25 additions & 18 deletions pkg/format/render_markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (

// MarkdownTreeRenderer renders a ContainerNode tree into a markdown documentation string
type MarkdownTreeRenderer struct {
HeaderPrefix string
HeaderPrefix string
PropsDescription string
}

// RenderTree renders a ContainerNode tree into a markdown documentation string
Expand All @@ -20,7 +21,6 @@ func (r MarkdownTreeRenderer) RenderTree(root *ContainerNode, scheme string) str

queryFields := make([]*FieldInfo, 0, len(root.Items))
urlFields := make([]*FieldInfo, URLPath+1)
fieldsPrinted := make(map[string]bool)

for _, node := range root.Items {
field := node.Field()
Expand All @@ -38,6 +38,27 @@ func (r MarkdownTreeRenderer) RenderTree(root *ContainerNode, scheme string) str
}
}

r.writeURLFields(&sb, urlFields, scheme)

sort.SliceStable(queryFields, func(i, j int) bool {
return queryFields[i].Required && !queryFields[j].Required
})

r.writeHeader(&sb, "Query/Param Props")
sb.WriteString(r.PropsDescription)
sb.WriteRune('\n')
for _, field := range queryFields {
r.writeFieldPrimary(&sb, field)
r.writeFieldExtras(&sb, field)
sb.WriteRune('\n')
}

return sb.String()
}

func (r MarkdownTreeRenderer) writeURLFields(sb *strings.Builder, urlFields []*FieldInfo, scheme string) {
fieldsPrinted := make(map[string]bool)

sort.SliceStable(urlFields, func(i, j int) bool {
if urlFields[i] == nil || urlFields[j] == nil {
return false
Expand All @@ -56,12 +77,12 @@ func (r MarkdownTreeRenderer) RenderTree(root *ContainerNode, scheme string) str
return urlPartA < urlPartB
})

r.writeHeader(&sb, "URL Fields")
r.writeHeader(sb, "URL Fields")
for _, field := range urlFields {
if field == nil || fieldsPrinted[field.Name] {
continue
}
r.writeFieldPrimary(&sb, field)
r.writeFieldPrimary(sb, field)

sb.WriteString(" URL part: <code class=\"service-url\">")

Expand Down Expand Up @@ -109,20 +130,6 @@ func (r MarkdownTreeRenderer) RenderTree(root *ContainerNode, scheme string) str

fieldsPrinted[field.Name] = true
}

sort.SliceStable(queryFields, func(i, j int) bool {
return queryFields[i].Required && !queryFields[j].Required
})

r.writeHeader(&sb, "Query/Param Props")
sb.WriteString("Props can be either supplied using the params argument, or through the URL using \n`?key=value&key=value` etc.\n\n")
for _, field := range queryFields {
r.writeFieldPrimary(&sb, field)
r.writeFieldExtras(&sb, field)
sb.WriteRune('\n')
}

return sb.String()
}

func (MarkdownTreeRenderer) writeFieldExtras(sb *strings.Builder, field *FieldInfo) {
Expand Down
67 changes: 67 additions & 0 deletions pkg/format/render_markdown_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package format

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
gformat "github.com/onsi/gomega/format"
)

var _ = Describe("RenderMarkdown", func() {
gformat.CharactersAroundMismatchToInclude = 30

It("should render the expected output based on config reflection/tags", func() {
actual := testRenderTee(MarkdownTreeRenderer{HeaderPrefix: `### `}, &struct {
Name string `default:"notempty"`
Host string `url:"host"`
}{})

expected := `
### URL Fields

* __Host__ (**Required**)
URL part: <code class="service-url">mock://<strong>host</strong>/</code>
### Query/Param Props


* __Name__
Default: `[1:] + "`notempty`" + `

`

Expect(actual).To(Equal(expected))
})

It("should render url paths in sorted order", func() {
actual := testRenderTee(MarkdownTreeRenderer{HeaderPrefix: `### `}, &struct {
Host string `url:"host"`
Path1 string `url:"path1"`
Path3 string `url:"path3"`
Path2 string `url:"path2"`
}{})

expected := `
### URL Fields

* __Host__ (**Required**)
URL part: <code class="service-url">mock://<strong>host</strong>/path1/path2/path3</code>
* __Path1__ (**Required**)
URL part: <code class="service-url">mock://host/<strong>path1</strong>/path2/path3</code>
* __Path2__ (**Required**)
URL part: <code class="service-url">mock://host/path1/<strong>path2</strong>/path3</code>
* __Path3__ (**Required**)
URL part: <code class="service-url">mock://host/path1/path2/<strong>path3</strong></code>
### Query/Param Props


`[1:] // Remove initial newline

Expect(actual).To(Equal(expected))
})
})

/*

* __TestEnum__
Default: `+"`None`"+`
Possible values: `+"`None`, `Foo`, `Bar`"+`
*/
11 changes: 11 additions & 0 deletions pkg/format/renderer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package format

// Renderer renders a supplied node tree to a string
type Renderer interface {
// RenderTree renders a ContainerNode tree into a ansi-colored console string
RenderTree(root *ContainerNode, scheme string) string
}

func testRenderTee(r Renderer, v interface{}) string {
return r.RenderTree(getRootNode(v), "mock")
}
7 changes: 6 additions & 1 deletion pkg/types/service_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package types

import "net/url"

// Enummer contains fields that have associated EnumFormatter instances
type Enummer interface {
Enums() map[string]EnumFormatter
}

// ServiceConfig is the common interface for all types of service configurations
type ServiceConfig interface {
Enummer
GetURL() *url.URL
SetURL(*url.URL) error
Enums() map[string]EnumFormatter
}

// ConfigQueryResolver is the interface used to get/set and list service config query fields
Expand Down