Skip to content

Commit

Permalink
reformat markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
crhntr committed Feb 23, 2025
1 parent b8d02e9 commit ecad3f2
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 129 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,26 @@ For larger complete examples, see:
## Documentation

### Introduction

- [Getting Started](./docs/getting_started.md)
- [Notes on Integration with Existing Projects](./docs/integrating.md)
- [Writing Template Names](./docs/template_names.md)

### Reference

- [Call Parameters](./docs/call_parameters.md)
- [Call Results](./docs/call_results.md)
- [Templates Variable](./docs/templates_variable.md)
- [Template Action Type-Checking](./docs/action_type_checking.md)
- [Known Issues](./docs/known_issues.md)

### Testing

- [Testing_Hypertext](./docs/testing_hypertext.md)
- [Testing_the_Receiver](./docs/testing_the_receiver.md)

### Philosophy & Vision

- [Manifesto](./docs/manifesto.md)
- [Motivation](./docs/motivation.md)
- Goals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ func (T) F(context.Context, Session, int) any { return nil }
func (T) Author(int) (Session, error) { return Session{}, nil }

func main() {}

-- template.go --
package main

Expand Down
16 changes: 8 additions & 8 deletions cmd/muxt/testdata/form_basic_fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ package main
import "time"

type (
T struct{
T struct {
spy func(form In) In
}
In struct {
Expand Down Expand Up @@ -96,20 +96,20 @@ func Test(t *testing.T) {

service := T{
spy: func(form In) In {
return form
},
return form
},
}
mux := http.NewServeMux()
TemplateRoutes(mux, service)
mux.ServeHTTP(rec, req)

res := rec.Result()

body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
t.Log(string(body))
body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
t.Log(string(body))

if res.StatusCode != http.StatusOK {
t.Fatalf("exp %q got %q", http.StatusText(http.StatusOK), http.StatusText(res.StatusCode))
Expand Down
3 changes: 1 addition & 2 deletions cmd/muxt/testdata/templates_multiple_embed_lines.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ var formHTML embed.FS
var templates = template.Must(template.ParseFS(formHTML, "*"))

type T struct{}

-- cmd/main.go --
package main

import "example.com/server"

var _ = server.T{}

func main() {}
func main() {}
3 changes: 1 addition & 2 deletions cmd/muxt/testdata/templates_multiple_globs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,11 @@ var formHTML embed.FS
var templates = template.Must(template.ParseFS(formHTML, "*"))

type T struct{}

-- cmd/main.go --
package main

import server "example.com"

var _ = server.TemplateRoutes

func main() {}
func main() {}
1 change: 0 additions & 1 deletion cmd/muxt/testdata/templates_multiple_parsefs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ var indexHTML embed.FS
var formHTML embed.FS

var templates = template.Must(template.Must(template.ParseFS(formHTML, "*")).ParseFS(indexHTML, "*"))

-- cmd/main.go --
package main

Expand Down
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# Documentation

### Introduction

- [Getting Started](./getting_started.md)
- [Notes on Integration with Existing Projects](./integrating.md)
- [Writing Template Names](./template_names.md)

### Reference

- [Call Parameters](./call_parameters.md)
- [Call Results](./call_results.md)
- [Templates Variable](./templates_variable.md)
- [Template Action Type-Checking](./action_type_checking.md)
- [Known Issues](./known_issues.md)

### Testing

- [Testing_Hypertext](./testing_hypertext.md)
- [Testing_the_Receiver](./testing_the_receiver.md)

### Philosophy & Vision

- [Manifesto](./manifesto.md)
- [Motivation](./motivation.md)
- Goals:
Expand Down
3 changes: 2 additions & 1 deletion docs/action_type_checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Avoid using the empty interface and you'll probably be fine.

If you wanna check out the code, it is in ./internal/templatetype.
At some point I'd like to publish this package separately.
I also want to support explicitly setting a template type via `gotype: ` comments that GoLand (by JetBrains) uses for tab completion.
I also want to support explicitly setting a template type via `gotype: ` comments that GoLand (by JetBrains) uses for
tab completion.

I also would like to extend this code to create better template documentation and maybe a storybook kind thing...
someday.
20 changes: 12 additions & 8 deletions docs/call_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@ There are three parameters you can pass to a method that always generate the sam

### Default Mapping

If you don't provide a named type with `--receiver-type`, muxt will try use the following default types for the generated interface methods.
If you don't provide a named type with `--receiver-type`, muxt will try use the following default types for the
generated interface methods.

- `ctx` -> `http.Request.Context`
- `request` -> `*http.Request`
- `response` -> `http.ResponseWriter`
- `form` -> `url.Values`
- (named path values) -> `string` (i.e. "/some/{value}" where the identifier "value" now a variable name in the call scope)
- `form` -> `url.Values`
- (named path values) -> `string` (i.e. "/some/{value}" where the identifier "value" now a variable name in the call
scope)

The method will return `any`.
This result type does not play well with `muxt check`.
You should set a `--receiver-type`.


#### Example without Receiver Type

Using some of the above, the generated code will look something like this.

Given `{{define "GET /project/{projectID}/task/{taskID} F(ctx, response, request, projectID, taskID)"}}Hello, world!{{end}}`,
Given
`{{define "GET /project/{projectID}/task/{taskID} F(ctx, response, request, projectID, taskID)"}}Hello, world!{{end}}`,
then you will get this:

```go
Expand All @@ -36,8 +38,8 @@ type RoutesReceiver interface {

### Example with Receiver Type

Now, say you provide `--receiver-type=Server`, muxt now will generate parsers in the handler and the generated interface will look like this

Now, say you provide `--receiver-type=Server`, muxt now will generate parsers in the handler and the generated interface
will look like this

```go
package server
Expand All @@ -57,7 +59,8 @@ func (_ Server) F(ctx context.Context, response http.ResponseWriter, request *ht

```

Given (the same as above) `{{define "GET /project/{projectID}/task/{taskID} F(ctx, response, request, projectID, taskID)"}}Hello, world!{{end}}`,
Given (the same as above)
`{{define "GET /project/{projectID}/task/{taskID} F(ctx, response, request, projectID, taskID)"}}Hello, world!{{end}}`,
then you will get this:

```go
Expand All @@ -71,6 +74,7 @@ type RoutesReceiver interface {
Muxt can generate form field and path parameter parsers for most basic Go types.

### Basic Kinds

- `int`
- `int64`
- `int32`
Expand Down
3 changes: 2 additions & 1 deletion docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ The (optionally) generated execute function uses the byte buffer.
The named empty interface RoutesReceiver has one method `F() string`.
The method signature was discovered by muxt by iterating over the methods on the named receiver `type Server`.

`func TemplateRoutes` is where generated (inline) http.HandlerFunc closures are mapped to http routes on the multiplexer.
`func TemplateRoutes` is where generated (inline) http.HandlerFunc closures are mapped to http routes on the
multiplexer.
It receives a pointer to the `http.ServeMux` if you have any route collisions from routes added on mux before
or after calling `TemplateRoutes`, `mux.HandleFunc` will panic.
The endpoint string `GET /` is cut out of the template name.
Expand Down
3 changes: 2 additions & 1 deletion docs/known_issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ You will see a warning if your template source is not valid HTML.

**Not all Go template features are supported.**
`muxt check` may give false negative type check errors.
If you find something you think is wrong, [please open an issue (or better yet PR a line to the following list)](https://github.com/crhntr/muxt/issues/new).
If you find something you think is
wrong, [please open an issue (or better yet PR a line to the following list)](https://github.com/crhntr/muxt/issues/new).

- methods or fields of type any may only be the final type in an action
- gotype comments used in the GoLand IDE from JetBrains products are not consulted
Expand Down
38 changes: 25 additions & 13 deletions docs/testing_hypertext.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
When building server-side applications with **Muxt**-generated routes, you often want to verify both the
**HTTP response** (e.g., status codes, headers)
and the **HTML/DOM output** (e.g., specific elements, text content, or errors).
The [`domtest`](https://github.com/crhntr/dom) package offers a convenient table-driven approach to do exactly that—asserting both HTTP and HTML-based outcomes in one cohesive flow.
The [`domtest`](https://github.com/crhntr/dom) package offers a convenient table-driven approach to do exactly
that—asserting both HTTP and HTML-based outcomes in one cohesive flow.

Below is an example test suite from the `blog_test` package, which illustrates how to integrate `domtest` with a Muxt route function named `Routes`.
A typical BDD test pattern emerges. Each test case specifies `Given` (setup, optional), `When` (the request, required), and `Then` (assertions, optional).
Below is an example test suite from the `blog_test` package, which illustrates how to integrate `domtest` with a Muxt
route function named `Routes`.
A typical BDD test pattern emerges. Each test case specifies `Given` (setup, optional), `When` (the request, required),
and `Then` (assertions, optional).
By leveraging `domtest`’s various assertion helpers, you can check DOM structure and content directly.

---
Expand All @@ -19,7 +22,6 @@ By leveraging `domtest`’s various assertion helpers, you can check DOM structu
This is an excerpt from a test.
To see the complete code run.


```shell
# I haven't actually run this script. It should get the gist across though.

Expand Down Expand Up @@ -141,7 +143,7 @@ func TestBlog(t *testing.T) {

- **`Given func(t *testing.T, app *fake.App)`**
Set up initial conditions—e.g., “ArticleReturns” to specify what happens when the route calls `app.Article(id)`.
-
-
- **`When func(t *testing.T) *http.Request`**
Creates the incoming request for this scenario (method, path, optional headers/body).

Expand All @@ -152,7 +154,8 @@ func TestBlog(t *testing.T) {
- `domtest.Fragment(...)` for partial responses, or
- a custom function that checks `response.StatusCode` directly.

Inside each `Then` block, you can use `require` or `assert` from [stretchr/testify](https://github.com/stretchr/testify) to fail the test if the expected DOM elements or status codes aren’t present.
Inside each `Then` block, you can use `require` or `assert` from [stretchr/testify](https://github.com/stretchr/testify)
to fail the test if the expected DOM elements or status codes aren’t present.

The final closure passed into Run is for you to call the muxt generated `routes` function.
It receives the fake receiver as a parameter.
Expand All @@ -172,6 +175,7 @@ Then: domtest.Document(func(t *testing.T, document spec.Document, app *fake.App)
}
}),
```

- The test ensures `app.Article(1)` was called.
- Looks up `<h1>` and `<p>` tags and verifies text content matches the expectation.

Expand All @@ -193,8 +197,10 @@ Then: domtest.Document(func(t *testing.T, document spec.Document, app *fake.App)
}),
},
```

- Sets up the domain method to return an error.
- Uses `domtest.QuerySelector("#error-message", ...)` to confirm the `<div id="error-message">` or similar element contains the string `"lemon"`.
- Uses `domtest.QuerySelector("#error-message", ...)` to confirm the `<div id="error-message">` or similar element
contains the string `"lemon"`.

### Example: Partial Responses [HTMX](http://htmx.org/)

Expand All @@ -218,10 +224,10 @@ Although the Document parser will allow incomplete documents, you may want to te
}),
},
```

- Simulates an **HTMX** request by adding `HX-Request: true`.
- Uses `domtest.Fragment(atom.Body, ...)` to parse only a `<body>` snippet or partial, checking content.


## Why vibe with `muxt` + `domtest`

1. **One-Stop Testing**
Expand All @@ -231,10 +237,12 @@ Although the Document parser will allow incomplete documents, you may want to te
- Check domain calls (e.g., `ArticleArgsForCall(0) == 1`) and the rendered DOM (the `<h1>` or error messages).

3. **Minimal Overhead**
- `domtest` sets up a structure that’s easy to read, maintain, and expand. Adding new test cases or scenarios is straightforward.
- `domtest` sets up a structure that’s easy to read, maintain, and expand. Adding new test cases or scenarios is
straightforward.

4. **Supports TDD/BDD**
- The table-driven approach with `Given`, `When`, and `Then` aligns naturally with Behavior-Driven Development or Extreme Programming’s quick feedback loop.
- The table-driven approach with `Given`, `When`, and `Then` aligns naturally with Behavior-Driven Development or
Extreme Programming’s quick feedback loop.

## Tips

Expand All @@ -243,12 +251,16 @@ Although the Document parser will allow incomplete documents, you may want to te
(e.g., “viewing the home page,” “the page has an error,” “when the id is not an integer.”)

2. **Use Mocking Tools**
- For more complex domain interactions, consider a mocking library like [counterfeiter](https://github.com/maxbrunsfeld/counterfeiter), generating stubs for your Muxt receiver methods.
- For more complex domain interactions, consider a mocking library
like [counterfeiter](https://github.com/maxbrunsfeld/counterfeiter), generating stubs for your Muxt receiver
methods.

3. **Keep Tests Atomic**
- Each scenario should test one major idea: e.g., “user not logged in -> unauthorized,” or “invalid input -> show error.” Avoid piling too many steps into a single test.
- Each scenario should test one major idea: e.g., “user not logged in -> unauthorized,” or “invalid input -> show
error.” Avoid piling too many steps into a single test.

4. **Combine with Muxt’s Type Checking**
- If you’re using Muxt’s static type check feature, you’ll get extra assurance that your templates, route parameters, and domain method signatures align correctly before even hitting these tests.
- If you’re using Muxt’s static type check feature, you’ll get extra assurance that your templates, route
parameters, and domain method signatures align correctly before even hitting these tests.

_(this article was mostly generated using an LLM model)_
6 changes: 4 additions & 2 deletions docs/testing_the_receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ type UpdateArticle {

## When You Really Need http.ResponseWriter

There are some cases (like streaming large file downloads or sending a specific header) where passing the response is unavoidable.
There are some cases (like streaming large file downloads or sending a specific header) where passing the response is
unavoidable.
In those situations remember to assert WriteHeader is called.
Make sure to set relevant headers and set the status code but dont' call `response.Write`. That's for execute.

Expand All @@ -61,4 +62,5 @@ func (MyReceiver) UserSettings(ctx context.Context, SessionClaims, userID int) U
```

When the second result from `SessionRedirectUnauthenticated` returns `true`, the next function is called.
When the second result from `SessionRedirectUnauthenticated` returns `false` the handler returns early and `UserSettings` is not called.
When the second result from `SessionRedirectUnauthenticated` returns `false` the handler returns early and
`UserSettings` is not called.
Loading

0 comments on commit ecad3f2

Please sign in to comment.