Files
Bose-SoundTouch/pkg/service/testing/fakespeaker/fakespeaker_test.go
T
Tobias GesellchenandClaude Opus 4.7 bb71253690 feat(screenshots): add headless-Chrome capture pipeline with fake speaker
Refreshes docs/images/ui-{settings,devices,sync,migration}.png by
driving the web UI in chromedp against a synthetic speaker, so
documentation can be regenerated without real hardware and without
leaking personal data from the local network.

Three independent pieces:

- pkg/service/testing/fakespeaker — embeddable library serving the
  HTTP and telnet surface the migration wizard probes (/info,
  /presets, /recents and a getpdo CurrentSystemConfiguration reply
  that places the device on the unmigrated happy path).
- cmd/dummy-speaker — thin CLI wrapping the library; self-registers
  with a running service via POST /setup/devices.
- scripts/screenshots — chromedp runner driven by a JSON manifest;
  decoupled from speaker/service setup so it can target any backend
  URL. run.sh orchestrates a one-shot end-to-end capture and seeds
  settings.json with a generic hostname plus discovery disabled to
  keep real-network state out of the captures.

Captures are at DPR=2 for retina-sharp text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 20:37:23 +02:00

65 lines
1.2 KiB
Go

package fakespeaker
import (
"context"
"encoding/xml"
"io"
"net/http"
"testing"
"time"
)
func TestFakeSpeakerServesFixtures(t *testing.T) {
s, err := Start(Config{})
if err != nil {
t.Fatalf("start: %v", err)
}
t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = s.Stop(ctx)
})
cases := []struct {
path string
root string
}{
{"/info", "info"},
{"/presets", "presets"},
{"/recents", "recents"},
}
for _, tc := range cases {
t.Run(tc.path, func(t *testing.T) {
resp, err := http.Get("http://" + s.HTTPAddr() + tc.path) //nolint:noctx
if err != nil {
t.Fatalf("get %s: %v", tc.path, err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
t.Fatalf("status = %d, want 200", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("read body: %v", err)
}
var root struct {
XMLName xml.Name
}
if err := xml.Unmarshal(body, &root); err != nil {
t.Fatalf("parse XML: %v\n%s", err, body)
}
if root.XMLName.Local != tc.root {
t.Fatalf("root element = %q, want %q", root.XMLName.Local, tc.root)
}
})
}
}