Add test servers

This commit is contained in:
Ben Pate 2021-11-10 18:28:39 -07:00
parent de228177bf
commit 3bd85e1e8d
26 changed files with 41863 additions and 187 deletions

View File

@ -1,41 +0,0 @@
<html>
<head>
<script src="../../src/htmx.js"></script>
<script src="../../src/ext/sse.js"></script>
<script>
htmx.createEventSource = function(url){
return new EventSource(url, {withCredentials:false})
}
</script>
<style>
*{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
background-color: #eeeeee;
}
.container {
padding: 10px;
border: solid 1px gray;
margin-bottom: 20px;
background-color:white;
}
.bold {
font-weight:bold;
}
</style>
</head>
<body>
<div id="page" hx-ext="sse" sse-url="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
<h3>Multiple Listeners. message only</h3>
<div class="container" sse-swap="Event1">Waiting for Posts in Event1 channel...</div>
<div class="container" sse-swap="Event2">Waiting for Posts in Event2 channel...</div>
<div class="container" sse-swap="Event3">Waiting for Posts in Event3 channel...</div>
<div class="container" sse-swap="Event4">Waiting for Posts in Event4 channel...</div>
</div>
</body>
</html>

View File

@ -1,50 +0,0 @@
<html>
<head>
<script src="../../src/htmx.js"></script>
<script src="../../src/ext/sse.js"></script>
<script>
htmx.createEventSource = function(url){
return new EventSource(url, {withCredentials:false})
}
</script>
<style>
*{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
background-color: #eeeeee;
}
.container {
padding: 10px;
border: solid 1px gray;
margin-bottom: 20px;
background-color:white;
}
.htmx-settling {
border:solid 3px red!important;
padding:8px!important;
}
.bold {
font-weight:bold;
}
</style>
</head>
<body>
<div id="page">
<h3>Settling Options</h3>
<div hx-ext="sse" sse-url="http://localhost/comments.html">
<div class="container" sse-swap="message" hx-swap="settle:100ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" hx-swap="settle:200ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" hx-swap="settle:300ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" hx-swap="settle:400ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" hx-swap="settle:500ms">Waiting for Comments...</div>
</div>
</div>
</body>
</html>

View File

@ -1,53 +0,0 @@
<html>
<head>
<script src="../../src/htmx.js"></script>
<script src="../../src/ext/sse.js"></script>
<script>
htmx.createEventSource = function(url){
return new EventSource(url, {withCredentials:false})
}
</script>
<style>
*{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
background-color: #eeeeee;
}
.container {
padding: 10px;
border: solid 1px gray;
margin-bottom: 20px;
background-color:white;
}
.bold {
font-weight:bold;
}
</style>
</head>
<body>
<div id="page" hx-ext="sse" sse-url="http://localhost/posts.html?types=event1%2cevent2%2cevent3%2cevent4">
<h3>Testing Triggers</h3>
<div id="first" class="container" hx-get="http://localhost/page/random" hx-trigger="sse:event1">Waiting for Posts in Event1 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:event2">Waiting for Posts in Event2 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:event3">Waiting for Posts in Event3 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:event4">Waiting for Posts in Event4 channel...</div>
</div>
</body>
<!--script>
setInterval(function() {
htmx.trigger("#first", "sse:event1")
/*
var e = new Event("event1")
var id = document.getElementById("first")
id.dispatchEvent(e)
console.log("SENT ***************************",id, e)
*/
}, 500)
</script-->
</html>

View File

@ -1,43 +0,0 @@
<html>
<head>
<script src="../../src/htmx.js"></script>
<script src="../../src/ext/sse.js"></script>
<script>
// "withCredentials:false" is necessary to circumvent CORS restrictions
htmx.createEventSource = function(url){
return new EventSource(url, {withCredentials:false})
}
</script>
<style>
*{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
background-color: #eeeeee;
}
.container {
padding: 10px;
border: solid 1px gray;
margin-bottom: 20px;
background-color:white;
}
.bold {
font-weight:bold;
}
</style>
</head>
<body>
<div id="page">
<h3>Multiple Listeners. message only</h3>
<div class="container" hx-ext="sse" sse-url="http://localhost/posts.html" sse-swap="message">Waiting for Posts...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/comments.html" sse-swap="message">Waiting for Comments...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/albums.html" sse-swap="message">Waiting for Albums...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/todos.html" sse-swap="message">Waiting for ToDos...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/users.html" sse-swap="message">Waiting for Users...</div>
</div>
</body>
</html>

View File

@ -0,0 +1,43 @@
# Server Sent Events - Test Server
This package implements a simple server that generates Server Sent Events for your test pages to read. It streams fake data from [jsonplaceholder](https://jsonplaceholder.typicode.com) to your website on a semi-regular schedule.
## How to Use This Server
1. If you do not already have Go (version 1.17 or higher) installed on your machine, you can download an installation for your machine from [the Go website](https://golang.org)
2. Open up a terminal window and navigate to this directory. Start up the WebSocket server by typing `go run server.go`
3. Open your web browser to [http://localhost](http://localhost) to run the manual tests. Huzzah!
## JSON Event Streams
Streams random JSON records every second (or so) to your client.
* `/posts.json`
* `/comments.json`
* `/albums.json`
* `/photos.json`
* `/todos.json`
* `/users.json`
## HTML Event Streams
Streams random HTML fragments every second (or so) to your client. These streams are used by the manual htmx tests.
* `/posts.html`
* `/comments.html`
* `/albums.html`
* `/photos.html`
* `/todos.html`
* `/users.html`
## Specifying Event Types
You can add a `type=` parameter to your URLs to specify the event name(s) that you want the server to use. You can specify multiple names in a comma separated list and the server will alternate between them. If you do not specify a type, then the default message name of `message` is used.
## About
This server is also published independently at [https://github.com/benpate/sseplaceholder]
It is inspired by [jsonplaceholder](https://jsonplaceholder.typicode.com) -- *"a free online REST API that you can use whenever you need some fake data."*

9
test/servers/sse/go.mod Normal file
View File

@ -0,0 +1,9 @@
module github.com/benpate/sseplaceholder
go 1.16
require (
github.com/benpate/derp v0.20.0
github.com/benpate/htmlconv v0.3.0
github.com/labstack/echo/v4 v4.1.17
)

55
test/servers/sse/go.sum Normal file
View File

@ -0,0 +1,55 @@
github.com/benpate/derp v0.20.0 h1:birPylsYOKkSMrcvNQEp+CmB7Dr1jnum8UXKv4oJvY8=
github.com/benpate/derp v0.20.0/go.mod h1:ENjMpkMmxn9gAulMAggElVcD2kNrsbjFhoZK8fUlCxY=
github.com/benpate/htmlconv v0.3.0 h1:UzIONnBJWtzBB3pYfKxlkq5JCsMq/+ImpAqNEmvFGcQ=
github.com/benpate/htmlconv v0.3.0/go.mod h1:9P8tQ62E5L/KF2wTNI0szRTKs3b9WlOHj4Gxmp66rf4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/labstack/echo/v4 v4.1.17 h1:PQIBaRplyRy3OjwILGkPg89JRtH2x5bssi59G2EL3fo=
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

281
test/servers/sse/server.go Normal file
View File

@ -0,0 +1,281 @@
package main
import (
"bytes"
_ "embed"
"encoding/json"
"fmt"
"html/template"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
"github.com/benpate/derp"
"github.com/benpate/htmlconv"
"github.com/labstack/echo/v4"
)
type formatFunc func(interface{}) string
//go:embed static/data.json
var dataBytes []byte
func main() {
rand.Seed(time.Now().UnixNano())
/// Load configuration file
var data map[string][]interface{}
if err := json.Unmarshal(dataBytes, &data); err != nil {
panic("Could not unmarshal data: " + err.Error())
}
/// Configure Web Server
e := echo.New()
e.Static("/", "static")
e.Static("/htmx", "../../../src")
// JSON Event Streams
e.GET("/posts.json", handleStream(makeStream(data["posts"], jsonFormatFunc)))
e.GET("/comments.json", handleStream(makeStream(data["comments"], jsonFormatFunc)))
e.GET("/photos.json", handleStream(makeStream(data["comments"], jsonFormatFunc)))
e.GET("/albums.json", handleStream(makeStream(data["albums"], jsonFormatFunc)))
e.GET("/todos.json", handleStream(makeStream(data["todos"], jsonFormatFunc)))
e.GET("/users.json", handleStream(makeStream(data["users"], jsonFormatFunc)))
// HTML Event Streams (with HTMX extension tags)
e.GET("/posts.html", handleStream(makeStream(data["posts"], postTemplate())))
e.GET("/comments.html", handleStream(makeStream(data["comments"], commentTemplate())))
e.GET("/photos.json", handleStream(makeStream(data["comments"], jsonFormatFunc)))
e.GET("/albums.html", handleStream(makeStream(data["albums"], albumTemplate())))
e.GET("/todos.html", handleStream(makeStream(data["todos"], todoTemplate())))
e.GET("/users.html", handleStream(makeStream(data["users"], userTemplate())))
e.OPTIONS("/page/random", func(ctx echo.Context) error {
ctx.Response().Header().Add("Connection", "keep-alive") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Origin", "*") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Methods", "GET") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Credentials", "true") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Headers", "*") // CORS headers
ctx.NoContent(200)
return nil
})
e.GET("/page/random", func(ctx echo.Context) error {
return pageHandler(ctx, rand.Int())
})
e.GET("/page/:number", func(ctx echo.Context) error {
pageNumber, err := strconv.Atoi(ctx.Param("number"))
if err != nil {
pageNumber = 1
}
return pageHandler(ctx, pageNumber)
})
e.GET("/revealed/:number", func(ctx echo.Context) error {
pageNumber, err := strconv.Atoi(ctx.Param("number"))
if err != nil {
pageNumber = 1
}
thisPage := strconv.Itoa(pageNumber)
nextPage := strconv.Itoa(pageNumber + 1)
random := strconv.Itoa(rand.Int())
template := htmlconv.CollapseWhitespace(`
<div class="container" hx-get="/revealed/%s" hx-swap="afterend limit:10" hx-trigger="revealed">
This is page %s<br><br>
Randomly generated <b>HTML</b> %s<br><br>
I wish I were a haiku.
</div>`)
content := fmt.Sprintf(template, nextPage, thisPage, random)
return ctx.HTML(200, content)
})
e.Logger.Fatal(e.Start(":80"))
}
func pageHandler(ctx echo.Context, page int) error {
pageString := strconv.Itoa(page)
random := strconv.Itoa(rand.Int())
template := htmlconv.CollapseWhitespace(`
<div>
This is page %s<br><br>
Randomly generated <b>HTML</b> %s<br><br>
I wish I were a haiku.
</div>`)
content := fmt.Sprintf(template, pageString, random)
ctx.Response().Header().Add("Access-Control-Allow-Origin", "*") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Methods", "GET") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Headers", "*") // CORS headers
ctx.Response().Header().Add("Access-Control-Allow-Credentials", "true") // CORS headers
return ctx.HTML(200, content)
}
func postTemplate() formatFunc {
// return templateFormatFunc("post.html", "DDD")
return templateFormatFunc("post.html", `
<div>
<div class="bold">Post: {{.title}}</div>
<div>{{.body}}</div>
<div>id: {{.id}}</div>
<div>user: {{.userId}}</div>
<div>event: [[eventType]]</div>
</div>`)
}
func commentTemplate() formatFunc {
// return templateFormatFunc("comment.html", "CCC")
return templateFormatFunc("comment.html", `
<div>
<div class="bold">Comment: {{.name}}</div>
<div>{{.email}}</div>
<div>{{.body}}</div>
<div>event: [[eventType]]</div>
</div>`)
}
func albumTemplate() formatFunc {
return templateFormatFunc("album.html", `
<div>
<div class="bold">Album: {{.title}}</div>
<div>id: {{.id}}</div>
<div>event: [[eventType]]</div>
</div>`)
}
func todoTemplate() formatFunc {
return templateFormatFunc("todo.html", `
<div>
<div class="bold">ToDo:{{.id}}: {{.title}}</div>
<div>complete? {{.completed}}</div>
<div>event: [[eventType]]</div>
</div>`)
}
func userTemplate() formatFunc {
return templateFormatFunc("user.html", `
<div>
<div class="bold">User: {{.name}} / {{.username}}</div>
<div>{{.email}}</div>
<div>{{.address.street}} {{.address.suite}}<br>{{.address.city}}, {{.address.zipcode}}</div>
<div>event: [[eventType]]</div>
</div>`)
}
// handleStream creates an echo.HandlerFunc that streams events from an eventSource to client browsers
func handleStream(eventSource chan string) echo.HandlerFunc {
return func(ctx echo.Context) error {
w := ctx.Response().Writer
// Make sure that the writer supports flushing.
f, ok := w.(http.Flusher)
if !ok {
return derp.New(500, "handler.ServerSentEvent", "Streaming Not Supported")
}
c := ctx.Request().Context()
// Set the headers related to event streaming.
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Transfer-Encoding", "chunked")
w.Header().Set("Access-Control-Allow-Origin", "*")
types := []string{}
if param := ctx.QueryParam("types"); param != "" {
types = strings.Split(param, ",")
}
// Don't close the connection, instead loop endlessly.
for {
select {
case <-c.Done():
log.Println("HTTP connection just closed.")
return nil
case message := <-eventSource:
var eventType string
if len(types) > 0 {
eventType = types[rand.Int()%len(types)]
fmt.Fprintf(w, "event: %s\n", eventType)
}
message = strings.Replace(message, "\n", " ", -1)
message = strings.Replace(message, "[[eventType]]", eventType, 1)
fmt.Fprintf(w, "data: %s\n\n", message)
// Flush the response. This is only possible if the response supports streaming.
f.Flush()
}
}
}
}
// makeStream loops through an array of interfaces
func makeStream(data []interface{}, format formatFunc) chan string {
result := make(chan string)
go func() {
for {
for _, record := range data {
result <- format(record)
time.Sleep((time.Duration(rand.Int() % 500)) * time.Millisecond)
}
}
}()
return result
}
func jsonFormatFunc(data interface{}) string {
result, _ := json.Marshal(data)
return string(result)
}
func templateFormatFunc(name string, text string) formatFunc {
f, _ := template.New(name).Parse(htmlconv.CollapseWhitespace(text))
return func(data interface{}) string {
var buffer bytes.Buffer
f.Execute(&buffer, data)
return buffer.String()
}
}

View File

@ -0,0 +1,9 @@
<svg width="389" height="82" viewBox="0 0 389 82" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M182.001 28.178V14.089V0H175.001H168.001L167.964 6.75C167.944 10.462 167.667 27.563 167.349 44.75L166.77 76H174.386H182.001V57.073V38.145L184.636 36.073C188.115 33.336 194.3 33.299 197.001 36C198.861 37.86 199.001 39.333 199.001 57V76H206.587H214.173L213.837 54.75C213.535 35.659 213.298 33.155 211.501 30.105C208.475 24.967 204.818 22.732 198.589 22.216C191.69 21.644 188.955 22.326 185.078 25.589L182.001 28.178Z" fill="#333333"/>
<path d="M241.001 23V16.5V10H234.001H227.001V16.5V23H223.001H219.001V28.5V34H223.001H227.001L227.016 50.75C227.032 69.257 227.474 71.055 232.913 74.72C235.464 76.439 237.716 77 242.067 77C250.558 77 251.001 76.673 251.001 70.393V65L247.751 64.985C241.716 64.956 241.532 64.497 241.18 48.537L240.859 34H245.43H250.001V28.5V23H245.501H241.001Z" fill="#333333"/>
<path d="M299.429 27.201C298.577 25.984 296.579 24.316 294.99 23.494C290.431 21.137 282.086 21.581 276.65 24.472C272.192 26.842 272.001 26.863 272.001 24.972C272.001 23.197 271.301 23 265.001 23H258.001V49.5V76H265.001H272.001V57.455C272.001 39.098 272.026 38.884 274.456 36.455C276.22 34.69 278.003 34 280.801 34C287.696 34 288.001 34.966 288.001 56.829V76H295.501H303.001V57.455C303.001 39.098 303.026 38.884 305.456 36.455C308.565 33.345 314.127 33.126 317.001 36C318.861 37.86 319.001 39.333 319.001 57V76H326.501H334.001L333.995 56.75C333.992 45.986 333.509 35.737 332.9 33.5C330.814 25.849 325.253 21.998 316.301 22.004C310.396 22.008 305.023 24.22 302.24 27.794C301.098 29.259 300.832 29.203 299.429 27.201Z" fill="#333333"/>
<path d="M361.928 33.395C360.349 30.703 358.54 27.263 357.908 25.75C356.791 23.078 356.535 23 348.821 23C344.455 23 341.077 23.338 341.315 23.75C341.552 24.162 344.932 29.997 348.825 36.715L355.904 48.93L348.021 62.465L340.139 76H348.002H355.865L360.146 68.073C362.501 63.713 364.742 60.34 365.127 60.578C365.511 60.815 367.677 64.382 369.939 68.505L374.053 76H381.527C385.638 76 389.001 75.768 389.001 75.483C389.001 75.199 385.598 69.113 381.439 61.958L373.877 48.949L381.403 35.974L388.929 23L381.215 23.011L373.501 23.021L369.15 30.655L364.798 38.289L361.928 33.395Z" fill="#3D72D7"/>
<path d="M74.3377 44.7953C81.2334 24.3322 87.0228 6.91645 87.2037 6.09245C87.4555 4.94086 83.3472 4.52536 79.4177 4.29664L74.3028 4L61.7576 41.2516C54.8569 61.7407 48.9026 79.2903 48.5259 80.2521C47.9233 81.7873 48.5059 82 53.3199 82H61.7995L74.3377 44.7953Z" fill="#3D72D7"/>
<path d="M43.0572 35.8912V28.9816C43.0572 24.0716 42.6954 22.1 41.808 22.1649C41.1214 22.2158 31.5644 26.168 20.5712 30.9492L0.583636 39.6406L0.291818 45.6143L0 51.588L20.7791 60.3164C32.208 65.1166 41.8959 69.0378 42.3077 69.0298C42.7194 69.0218 43.0572 65.9206 43.0572 62.1372V55.2576L29.4747 50.4884L15.8921 45.7182L25.2273 42.4062C30.3611 40.5844 36.4733 38.3731 38.8098 37.4922L43.0572 35.8912Z" fill="#333333"/>
<path d="M134.986 39.5517L115.255 31.3467C104.403 26.8342 94.7381 22.854 93.7757 22.5005C92.2156 21.9282 92.0267 22.6143 92.0267 28.8627V35.8672L100.272 38.8216C104.806 40.4466 110.878 42.5471 113.763 43.4909C116.648 44.4338 119.01 45.3357 119.01 45.4935C119.01 45.6523 112.939 47.9145 105.518 50.5203L92.0267 55.2576V62.1372C92.0267 65.9206 92.3645 69.0218 92.7763 69.0298C93.188 69.0378 102.858 65.1455 114.263 60.3813L135 51.7179L134.993 45.6343L134.986 39.5517Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
<html>
<head>
<link rel="stylesheet" href="/stylesheet.css">
</head>
<body>
<div id="header"></div>
<div id="page">
<h1>SSE Test Server</h1>
<ul>
<li><a href="sse.html">Simple Test</a></li>
<li><a href="sse-multichannel.html">Multi-Channel Test</a></li>
<li><a href="sse-triggers.html">Event Trigger Test</a></li>
<li><a href="sse-target.html">Event Target Test</a></li>
<li><a href="sse-settle.html">Settling Test</a></li>
</ul>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<html>
<head>
<link rel="stylesheet" href="/stylesheet.css">
<script src="/htmx/htmx.js"></script>
<script src="/htmx/ext/sse.js"></script>
</head>
<body>
<div id="header"></div>
<div id="page" hx-ext="sse" sse-url="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
<h1><a href="index.html">SSE Test Server</a> - Multi-Channel Test</h1>
<h3>Description</h3>
<p>
This page connects to a single different Server Sent Event (SSE) stream, listening on events named "Event1", "Event2", "Event3", and "Event4".
Each separate kind of event should swap into a different container.
</p>
<h3>Example HTML</h3>
<pre class="code">
&lt;div hx-ext="sse" sse-url="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4"&gt;
&lt;div sse-swap="Event1"&gt;Waiting for Posts in Event1 channel...&lt;/div&gt;
&lt;/div&gt;
</pre>
<h3>Test Cases</h3>
<div class="container" sse-swap="Event1">Waiting for Posts in Event1 channel...</div>
<div class="container" sse-swap="Event2">Waiting for Posts in Event2 channel...</div>
<div class="container" sse-swap="Event3">Waiting for Posts in Event3 channel...</div>
<div class="container" sse-swap="Event4">Waiting for Posts in Event4 channel...</div>
</div>
</body>
</html>

View File

@ -0,0 +1,33 @@
<html>
<head>
<link rel="stylesheet" href="/stylesheet.css">
<script src="/htmx/htmx.js"></script>
<script src="/htmx/ext/sse.js"></script>
</head>
<body>
<div id="header"></div>
<div id="page">
<h1><a href="index.html">SSE Test Server</a> - Settling Test</h1>
<h3>Description</h3>
<p>
This page connects to a single different Server Sent Event (SSE) stream.
Multiple containers all listen for the same default "message" event name, but using different values for hx-swap.
</p>
<h3>Example HTML</h3>
<pre class="code">
&lt;div hx-ext="sse" sse-url="http://localhost/comments.html"&gt;
&lt;div sse-swap="message" hx-swap="settle:100ms"&gt;Waiting for Comments...&lt;/div&gt;
&lt;/div&gt;
</pre>
<h3>Test Cases</h3>
<div hx-ext="sse" sse-url="http://localhost/comments.html">
<div class="container" sse-swap="message" sse-settle="100ms" hx-swap="settle:100ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" sse-settle="200ms" hx-swap="settle:200ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" sse-settle="300ms" hx-swap="settle:300ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" sse-settle="400ms" hx-swap="settle:400ms">Waiting for Comments...</div>
<div class="container" sse-swap="message" sse-settle="500ms" hx-swap="settle:500ms">Waiting for Comments...</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,34 @@
<head>
<link rel="stylesheet" href="/stylesheet.css">
<script src="/htmx/htmx.js"></script>
<script src="/htmx/ext/sse.js"></script>
</head>
<body>
<div id="header"></div>
<div id="page" hx-ext="sse">
<h1><a href="index.html">SSE Test Server</a> - Event Target Test</h1>
<h3>Description</h3>
<p>
This page connects to several different different Server Sent Event (SSE) stream.
Contents from each stream are sent to a single DOM element.
</p>
<h3>Example HTML</h3>
<pre class="code">
&lt;div sse-connect="/users/sse.html" sse-target="#otherNode"&gt;&lt;/div&gt;
&lt;div id="otherNode"&gt;&lt;/div&gt;
</pre>
<h3>Test Cases</h3>
<h3>Multiple Listeners using HX-Target to write to a single target node.</h3>
<div sse-connect="/posts/sse.html" hx-target="#otherNode"></div>
<div sse-connect="/comments/sse.html" hx-target="#otherNode"></div>
<div sse-connect="/albums/sse.html" hx-target="#otherNode"></div>
<div sse-connect="/todos/sse.html" hx-target="#otherNode"></div>
<div sse-connect="/users/sse.html" hx-target="#otherNode"></div>
<div class="container" id="otherNode">Waiting for records from any of: Posts/Comments/Albums/ToDos/Users</div>
</div>
</body>
</html>

View File

@ -0,0 +1,31 @@
<html>
<head>
<link rel="stylesheet" href="/stylesheet.css">
<script src="/htmx/htmx.js"></script>
<script src="/htmx/ext/sse.js"></script>
</head>
<body>
<div id="header"></div>
<div id="page" hx-ext="sse" sse-url="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
<h1><a href="index.html">SSE Test Server</a> - Event Trigger Test</h1>
<h3>Description</h3>
<p>
This page connects to a single different Server Sent Event (SSE) streams, listening on events named "Event1", "Event2", "Event3", and "Event4".
Each event is used as a trigger for hx-get to load a random page from the server.
</p>
<h3>Example HTML</h3>
<pre class="code">
&lt;div hx-ext="sse" sse-url="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4"&gt;
&lt;div hx-get="http://localhost/page/random" hx-trigger="sse:Event1"&gt;Waiting for Posts...&lt;/div&gt;
&lt;/div&gt;
</pre>
<h3>Test Cases</h3>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:Event1">Waiting for Posts in Event1 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:Event2">Waiting for Posts in Event2 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:Event3">Waiting for Posts in Event3 channel...</div>
<div class="container" hx-get="http://localhost/page/random" hx-trigger="sse:Event4">Waiting for Posts in Event4 channel...</div>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<html>
<head>
<link rel="stylesheet" href="/stylesheet.css">
<script src="/htmx/htmx.js"></script>
<script src="/htmx/ext/sse.js"></script>
</head>
<body>
<div id="header"></div>
<div id="page">
<h1><a href="index.html">SSE Test Server</a> - Simple Test</h1>
<h3>Description</h3>
<p>
This page connects to several different Server Sent Event (SSE) streams, listening on the default event name "message".
Each stream should populate its own container.
</p>
<h3>Example HTML</h3>
<pre class="code">
&lt;div hx-ext="sse" sse-url="http://localhost/posts.html" sse-swap="message"&gt;Waiting for Posts...&lt;/div&gt;
</pre>
<h3>Test Cases</h3>
<div class="container" hx-ext="sse" sse-url="http://localhost/posts.html" sse-swap="message">Waiting for Posts...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/comments.html" sse-swap="message">Waiting for Comments...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/albums.html" sse-swap="message">Waiting for Albums...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/todos.html" sse-swap="message">Waiting for ToDos...</div>
<div class="container" hx-ext="sse" sse-url="http://localhost/users.html" sse-swap="message">Waiting for Users...</div>
</div>
</body>
</html>

View File

@ -0,0 +1,62 @@
*{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
box-sizing: border-box;
}
body {
background-color: white;
padding:0px;
margin:0px;
width:100%;
}
#header {
width:100%;
height: 100px;
background-image:url('white_transparent.svg');
background-position:left 50px center;
background-repeat:no-repeat;
background-size: 300px;
background-color:black;
}
#page {
margin: 50px;
}
.container {
padding: 10px;
border: solid 1px gray;
margin-bottom: 20px;
background-color:white;
}
pre.code {
font-family:'Courier New', Courier, monospace;
background-color: #333;
color: #0f0;
padding:30px 5px 30px 15px;
overflow-y:scroll;
display:block;
}
.htmx-settling {
border:solid 3px red!important;
padding:8px!important;
}
.bold {
font-weight:bold;
}
.demo {
padding:10px;
margin:20px 0px;
color:white;
background-color: #999;
height:100px;
}
a, a:visited {
color:#3465a4;
}

View File

@ -0,0 +1,9 @@
<svg width="391" height="82" viewBox="0 0 391 82" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M184.001 28.178V14.089V0H177.001H170.001L169.964 6.75C169.944 10.462 169.667 27.563 169.349 44.75L168.77 76H176.386H184.001V57.073V38.145L186.636 36.073C190.115 33.336 196.3 33.299 199.001 36C200.861 37.86 201.001 39.333 201.001 57V76H208.587H216.173L215.837 54.75C215.535 35.659 215.298 33.155 213.501 30.105C210.475 24.967 206.818 22.732 200.589 22.216C193.69 21.644 190.955 22.326 187.078 25.589L184.001 28.178Z" fill="#F5F5F5"/>
<path d="M243.001 23V16.5V10H236.001H229.001V16.5V23H225.001H221.001V28.5V34H225.001H229.001L229.016 50.75C229.032 69.257 229.474 71.055 234.913 74.72C237.464 76.439 239.716 77 244.067 77C252.558 77 253.001 76.673 253.001 70.393V65L249.751 64.985C243.716 64.956 243.532 64.497 243.18 48.537L242.859 34H247.43H252.001V28.5V23H247.501H243.001Z" fill="#F5F5F5"/>
<path d="M301.429 27.201C300.577 25.984 298.579 24.316 296.99 23.494C292.431 21.137 284.086 21.581 278.65 24.472C274.192 26.842 274.001 26.863 274.001 24.972C274.001 23.197 273.301 23 267.001 23H260.001V49.5V76H267.001H274.001V57.455C274.001 39.098 274.026 38.884 276.456 36.455C278.22 34.69 280.003 34 282.801 34C289.696 34 290.001 34.966 290.001 56.829V76H297.501H305.001V57.455C305.001 39.098 305.026 38.884 307.456 36.455C310.565 33.345 316.127 33.126 319.001 36C320.861 37.86 321.001 39.333 321.001 57V76H328.501H336.001L335.995 56.75C335.992 45.986 335.509 35.737 334.9 33.5C332.814 25.849 327.253 21.998 318.301 22.004C312.396 22.008 307.023 24.22 304.24 27.794C303.098 29.259 302.832 29.203 301.429 27.201Z" fill="#F5F5F5"/>
<path d="M363.928 33.395C362.349 30.703 360.54 27.263 359.908 25.75C358.791 23.078 358.535 23 350.821 23C346.455 23 343.077 23.338 343.315 23.75C343.552 24.162 346.932 29.997 350.825 36.715L357.904 48.93L350.021 62.465L342.139 76H350.002H357.865L362.146 68.073C364.501 63.713 366.742 60.34 367.127 60.578C367.511 60.815 369.677 64.382 371.939 68.505L376.053 76H383.527C387.638 76 391.001 75.768 391.001 75.483C391.001 75.199 387.598 69.113 383.439 61.958L375.877 48.949L383.403 35.974L390.929 23L383.215 23.011L375.501 23.021L371.15 30.655L366.798 38.289L363.928 33.395Z" fill="#3D72D7"/>
<path d="M75.1218 44.7953C82.0175 24.3322 87.8069 6.91645 87.9877 6.09245C88.2396 4.94086 84.1313 4.52536 80.2017 4.29664L75.0869 4L62.5417 41.2516C55.641 61.7407 49.6867 79.2903 49.31 80.2521C48.7073 81.7873 49.29 82 54.104 82H62.5836L75.1218 44.7953Z" fill="#3D72D7"/>
<path d="M43.8414 35.8912V28.9816C43.8414 24.0716 43.4796 22.1 42.5921 22.1649C41.9056 22.2158 32.3485 26.168 21.3553 30.9492L1.36779 39.6406L1.07597 45.6143L0.784149 51.588L21.5632 60.3164C32.9921 65.1166 42.6801 69.0378 43.0918 69.0298C43.5036 69.0218 43.8414 65.9206 43.8414 62.1372V55.2576L30.2588 50.4884L16.6763 45.7182L26.0114 42.4062C31.1453 40.5844 37.2575 38.3731 39.594 37.4922L43.8414 35.8912Z" fill="#F5F5F5"/>
<path d="M135.77 39.5517L116.039 31.3467C105.187 26.8342 95.5222 22.854 94.5598 22.5005C92.9998 21.9282 92.8109 22.6143 92.8109 28.8627V35.8672L101.056 38.8216C105.59 40.4466 111.662 42.5471 114.547 43.4909C117.433 44.4338 119.794 45.3357 119.794 45.4935C119.794 45.6523 113.723 47.9145 106.302 50.5203L92.8109 55.2576V62.1372C92.8109 65.9206 93.1487 69.0218 93.5604 69.0298C93.9722 69.0378 103.642 65.1455 115.047 60.3813L135.784 51.7179L135.777 45.6343L135.77 39.5517Z" fill="#F5F5F5"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

15
test/servers/ws/README.md Normal file
View File

@ -0,0 +1,15 @@
# WebSocket - Test Server
This package implements a bare-bones WebSocket server for testing htmx. It can be used in conjunction with the manual tests in the `/test/manual` directory.
## What It Does
This listens for incoming WebSocket connections coming in to ws://localhost:1323/echo. When it receives messages from any WebSocket client, it responds with that same content in a way that htmx can process. This means, that the response message will look like this: `<div id="idMessage" hx-swap-oob="true">{your message here}</div>`
## How to Use This Server
1. If you do not already have Go (version 1.17 or higher) installed on your machine, you can download an installation for your machine from [https://golang.org](the Go website)
2. Open up a terminal window and navigate to this directory. Start up the WebSocket server by typing `go run server.go`
3. Open your web browser to [http://localhost](http://localhost) to run the manual tests. Huzzah!

23
test/servers/ws/go.mod Normal file
View File

@ -0,0 +1,23 @@
module github.com/benpate/ghost
go 1.17
require (
github.com/labstack/echo/v4 v4.3.0
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
)

52
test/servers/ws/go.sum Normal file
View File

@ -0,0 +1,52 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/labstack/echo/v4 v4.3.0 h1:DCP6cbtT+Zu++K6evHOJzSgA2115cPMuCx0xg55q1EQ=
github.com/labstack/echo/v4 v4.3.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

65
test/servers/ws/server.go Normal file
View File

@ -0,0 +1,65 @@
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"golang.org/x/net/websocket"
)
func wsEcho(c echo.Context) error {
handler := websocket.Handler(func(ws *websocket.Conn) {
defer ws.Close()
/*
var done chan<- bool
defer func() {
close(done)
ws.Close()
}()
go func() {
for i := 0; ; i = i + 1 {
time.Sleep(10 * time.Second)
if done == nil {
return
}
websocket.Message.Send(ws, "ping #"+strconv.Itoa(i))
}
}()
*/
for {
msg := ""
if err := websocket.Message.Receive(ws, &msg); err != nil {
c.Logger().Error("receive", err)
return
}
response := `<div id="idMessage" hx-swap-oob="true">` + msg + `</div>`
if err := websocket.Message.Send(ws, response); err != nil {
c.Logger().Error("send", err)
return
}
}
})
handler.ServeHTTP(c.Response(), c.Request())
return nil
}
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Static("/", "../static")
e.Static("/htmx", "../../../src")
e.GET("/echo", wsEcho)
e.Logger.Fatal(e.Start(":1323"))
}

View File

@ -0,0 +1,9 @@
<svg width="389" height="82" viewBox="0 0 389 82" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M182.001 28.178V14.089V0H175.001H168.001L167.964 6.75C167.944 10.462 167.667 27.563 167.349 44.75L166.77 76H174.386H182.001V57.073V38.145L184.636 36.073C188.115 33.336 194.3 33.299 197.001 36C198.861 37.86 199.001 39.333 199.001 57V76H206.587H214.173L213.837 54.75C213.535 35.659 213.298 33.155 211.501 30.105C208.475 24.967 204.818 22.732 198.589 22.216C191.69 21.644 188.955 22.326 185.078 25.589L182.001 28.178Z" fill="#333333"/>
<path d="M241.001 23V16.5V10H234.001H227.001V16.5V23H223.001H219.001V28.5V34H223.001H227.001L227.016 50.75C227.032 69.257 227.474 71.055 232.913 74.72C235.464 76.439 237.716 77 242.067 77C250.558 77 251.001 76.673 251.001 70.393V65L247.751 64.985C241.716 64.956 241.532 64.497 241.18 48.537L240.859 34H245.43H250.001V28.5V23H245.501H241.001Z" fill="#333333"/>
<path d="M299.429 27.201C298.577 25.984 296.579 24.316 294.99 23.494C290.431 21.137 282.086 21.581 276.65 24.472C272.192 26.842 272.001 26.863 272.001 24.972C272.001 23.197 271.301 23 265.001 23H258.001V49.5V76H265.001H272.001V57.455C272.001 39.098 272.026 38.884 274.456 36.455C276.22 34.69 278.003 34 280.801 34C287.696 34 288.001 34.966 288.001 56.829V76H295.501H303.001V57.455C303.001 39.098 303.026 38.884 305.456 36.455C308.565 33.345 314.127 33.126 317.001 36C318.861 37.86 319.001 39.333 319.001 57V76H326.501H334.001L333.995 56.75C333.992 45.986 333.509 35.737 332.9 33.5C330.814 25.849 325.253 21.998 316.301 22.004C310.396 22.008 305.023 24.22 302.24 27.794C301.098 29.259 300.832 29.203 299.429 27.201Z" fill="#333333"/>
<path d="M361.928 33.395C360.349 30.703 358.54 27.263 357.908 25.75C356.791 23.078 356.535 23 348.821 23C344.455 23 341.077 23.338 341.315 23.75C341.552 24.162 344.932 29.997 348.825 36.715L355.904 48.93L348.021 62.465L340.139 76H348.002H355.865L360.146 68.073C362.501 63.713 364.742 60.34 365.127 60.578C365.511 60.815 367.677 64.382 369.939 68.505L374.053 76H381.527C385.638 76 389.001 75.768 389.001 75.483C389.001 75.199 385.598 69.113 381.439 61.958L373.877 48.949L381.403 35.974L388.929 23L381.215 23.011L373.501 23.021L369.15 30.655L364.798 38.289L361.928 33.395Z" fill="#3D72D7"/>
<path d="M74.3377 44.7953C81.2334 24.3322 87.0228 6.91645 87.2037 6.09245C87.4555 4.94086 83.3472 4.52536 79.4177 4.29664L74.3028 4L61.7576 41.2516C54.8569 61.7407 48.9026 79.2903 48.5259 80.2521C47.9233 81.7873 48.5059 82 53.3199 82H61.7995L74.3377 44.7953Z" fill="#3D72D7"/>
<path d="M43.0572 35.8912V28.9816C43.0572 24.0716 42.6954 22.1 41.808 22.1649C41.1214 22.2158 31.5644 26.168 20.5712 30.9492L0.583636 39.6406L0.291818 45.6143L0 51.588L20.7791 60.3164C32.208 65.1166 41.8959 69.0378 42.3077 69.0298C42.7194 69.0218 43.0572 65.9206 43.0572 62.1372V55.2576L29.4747 50.4884L15.8921 45.7182L25.2273 42.4062C30.3611 40.5844 36.4733 38.3731 38.8098 37.4922L43.0572 35.8912Z" fill="#333333"/>
<path d="M134.986 39.5517L115.255 31.3467C104.403 26.8342 94.7381 22.854 93.7757 22.5005C92.2156 21.9282 92.0267 22.6143 92.0267 28.8627V35.8672L100.272 38.8216C104.806 40.4466 110.878 42.5471 113.763 43.4909C116.648 44.4338 119.01 45.3357 119.01 45.4935C119.01 45.6523 112.939 47.9145 105.518 50.5203L92.0267 55.2576V62.1372C92.0267 65.9206 92.3645 69.0218 92.7763 69.0298C93.188 69.0378 102.858 65.1455 114.263 60.3813L135 51.7179L134.993 45.6343L134.986 39.5517Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,9 @@
<svg width="391" height="82" viewBox="0 0 391 82" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M184.001 28.178V14.089V0H177.001H170.001L169.964 6.75C169.944 10.462 169.667 27.563 169.349 44.75L168.77 76H176.386H184.001V57.073V38.145L186.636 36.073C190.115 33.336 196.3 33.299 199.001 36C200.861 37.86 201.001 39.333 201.001 57V76H208.587H216.173L215.837 54.75C215.535 35.659 215.298 33.155 213.501 30.105C210.475 24.967 206.818 22.732 200.589 22.216C193.69 21.644 190.955 22.326 187.078 25.589L184.001 28.178Z" fill="#F5F5F5"/>
<path d="M243.001 23V16.5V10H236.001H229.001V16.5V23H225.001H221.001V28.5V34H225.001H229.001L229.016 50.75C229.032 69.257 229.474 71.055 234.913 74.72C237.464 76.439 239.716 77 244.067 77C252.558 77 253.001 76.673 253.001 70.393V65L249.751 64.985C243.716 64.956 243.532 64.497 243.18 48.537L242.859 34H247.43H252.001V28.5V23H247.501H243.001Z" fill="#F5F5F5"/>
<path d="M301.429 27.201C300.577 25.984 298.579 24.316 296.99 23.494C292.431 21.137 284.086 21.581 278.65 24.472C274.192 26.842 274.001 26.863 274.001 24.972C274.001 23.197 273.301 23 267.001 23H260.001V49.5V76H267.001H274.001V57.455C274.001 39.098 274.026 38.884 276.456 36.455C278.22 34.69 280.003 34 282.801 34C289.696 34 290.001 34.966 290.001 56.829V76H297.501H305.001V57.455C305.001 39.098 305.026 38.884 307.456 36.455C310.565 33.345 316.127 33.126 319.001 36C320.861 37.86 321.001 39.333 321.001 57V76H328.501H336.001L335.995 56.75C335.992 45.986 335.509 35.737 334.9 33.5C332.814 25.849 327.253 21.998 318.301 22.004C312.396 22.008 307.023 24.22 304.24 27.794C303.098 29.259 302.832 29.203 301.429 27.201Z" fill="#F5F5F5"/>
<path d="M363.928 33.395C362.349 30.703 360.54 27.263 359.908 25.75C358.791 23.078 358.535 23 350.821 23C346.455 23 343.077 23.338 343.315 23.75C343.552 24.162 346.932 29.997 350.825 36.715L357.904 48.93L350.021 62.465L342.139 76H350.002H357.865L362.146 68.073C364.501 63.713 366.742 60.34 367.127 60.578C367.511 60.815 369.677 64.382 371.939 68.505L376.053 76H383.527C387.638 76 391.001 75.768 391.001 75.483C391.001 75.199 387.598 69.113 383.439 61.958L375.877 48.949L383.403 35.974L390.929 23L383.215 23.011L375.501 23.021L371.15 30.655L366.798 38.289L363.928 33.395Z" fill="#3D72D7"/>
<path d="M75.1218 44.7953C82.0175 24.3322 87.8069 6.91645 87.9877 6.09245C88.2396 4.94086 84.1313 4.52536 80.2017 4.29664L75.0869 4L62.5417 41.2516C55.641 61.7407 49.6867 79.2903 49.31 80.2521C48.7073 81.7873 49.29 82 54.104 82H62.5836L75.1218 44.7953Z" fill="#3D72D7"/>
<path d="M43.8414 35.8912V28.9816C43.8414 24.0716 43.4796 22.1 42.5921 22.1649C41.9056 22.2158 32.3485 26.168 21.3553 30.9492L1.36779 39.6406L1.07597 45.6143L0.784149 51.588L21.5632 60.3164C32.9921 65.1166 42.6801 69.0378 43.0918 69.0298C43.5036 69.0218 43.8414 65.9206 43.8414 62.1372V55.2576L30.2588 50.4884L16.6763 45.7182L26.0114 42.4062C31.1453 40.5844 37.2575 38.3731 39.594 37.4922L43.8414 35.8912Z" fill="#F5F5F5"/>
<path d="M135.77 39.5517L116.039 31.3467C105.187 26.8342 95.5222 22.854 94.5598 22.5005C92.9998 21.9282 92.8109 22.6143 92.8109 28.8627V35.8672L101.056 38.8216C105.59 40.4466 111.662 42.5471 114.547 43.4909C117.433 44.4338 119.794 45.3357 119.794 45.4935C119.794 45.6523 113.723 47.9145 106.302 50.5203L92.8109 55.2576V62.1372C92.8109 65.9206 93.1487 69.0218 93.5604 69.0298C93.9722 69.0378 103.642 65.1455 115.047 60.3813L135.784 51.7179L135.777 45.6343L135.77 39.5517Z" fill="#F5F5F5"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB