mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
41402ce524
The test app now responds to mouse clicks; clicking on one of the four sections of the app flips it to red color until clicked again. Add a Click method to the TestDriver interface, and implement it in both of the current drivers. Unfortunately, I failed at implementing it in X11 with the xdg library, after a few wasted hours. Instead, start relying on more external tools which are simple to use and not heavy to install. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
157 lines
3.8 KiB
Go
157 lines
3.8 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"image"
|
|
"image/png"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/chromedp/cdproto/runtime"
|
|
"github.com/chromedp/chromedp"
|
|
|
|
_ "gioui.org/unit" // the build tool adds it to go.mod, so keep it there
|
|
)
|
|
|
|
type JSTestDriver struct {
|
|
t *testing.T
|
|
|
|
// ctx is the chromedp context.
|
|
ctx context.Context
|
|
}
|
|
|
|
func (d *JSTestDriver) Start(t_ *testing.T, path string, width, height int) (cleanups []func()) {
|
|
d.t = t_
|
|
|
|
// First, build the app.
|
|
dir, err := ioutil.TempDir("", "gio-endtoend-js")
|
|
if err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
cleanups = append(cleanups, func() { os.RemoveAll(dir) })
|
|
|
|
// TODO(mvdan): This is inefficient, as we link the gogio tool every time.
|
|
// Consider options in the future. On the plus side, this is simple.
|
|
cmd := exec.Command("go", "run", ".", "-target=js", "-o="+dir, path)
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
d.t.Fatalf("could not build app: %s:\n%s", err, out)
|
|
}
|
|
|
|
// Second, start Chrome.
|
|
opts := append(chromedp.DefaultExecAllocatorOptions[:],
|
|
chromedp.Flag("headless", *headless),
|
|
|
|
// The default would be use-gl=desktop when there's a GPU we can
|
|
// use, falling back to use-gl=swiftshader otherwise or when we
|
|
// are running in headless mode. Swiftshader allows full WebGL
|
|
// support with just a CPU.
|
|
//
|
|
// Unfortunately, many Linux distros like Arch and Alpine
|
|
// package Chromium without Swiftshader, so we can't rely on the
|
|
// defaults above. use-gl=egl works on any machine with a GPU,
|
|
// even if we run Chrome in headless mode, which is OK for now.
|
|
//
|
|
// TODO(mvdan): remove all of this once these issues are fixed:
|
|
//
|
|
// https://bugs.archlinux.org/task/64307
|
|
// https://gitlab.alpinelinux.org/alpine/aports/issues/10920
|
|
chromedp.Flag("use-gl", "egl"),
|
|
)
|
|
|
|
actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
|
|
cleanups = append(cleanups, cancel)
|
|
|
|
ctx, cancel := chromedp.NewContext(actx,
|
|
// Send all logf/errf calls to t.Logf
|
|
chromedp.WithLogf(d.t.Logf),
|
|
)
|
|
cleanups = append(cleanups, cancel)
|
|
d.ctx = ctx
|
|
|
|
if err := chromedp.Run(ctx); err != nil {
|
|
if errors.Is(err, exec.ErrNotFound) {
|
|
d.t.Skipf("test requires Chrome to be installed: %v", err)
|
|
return
|
|
}
|
|
d.t.Fatal(err)
|
|
}
|
|
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
|
switch ev := ev.(type) {
|
|
case *runtime.EventConsoleAPICalled:
|
|
switch ev.Type {
|
|
case "log", "info", "warning", "error":
|
|
var args strings.Builder
|
|
for i, arg := range ev.Args {
|
|
if i > 0 {
|
|
args.WriteString(", ")
|
|
}
|
|
args.Write(arg.Value)
|
|
}
|
|
d.t.Logf("console %s: %s", ev.Type, args.String())
|
|
}
|
|
}
|
|
})
|
|
|
|
// Third, serve the app folder, set the browser tab dimensions, and
|
|
// navigate to the folder.
|
|
ts := httptest.NewServer(http.FileServer(http.Dir(dir)))
|
|
cleanups = append(cleanups, ts.Close)
|
|
|
|
if err := chromedp.Run(ctx,
|
|
chromedp.EmulateViewport(int64(width), int64(height)),
|
|
chromedp.Navigate(ts.URL),
|
|
); err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
|
|
if err := chromedp.Run(ctx,
|
|
chromedp.WaitReady("canvas", chromedp.ByQuery),
|
|
); err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
// TODO(mvdan): synchronize with the app instead
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
return cleanups
|
|
}
|
|
|
|
func (d *JSTestDriver) Screenshot() image.Image {
|
|
var buf []byte
|
|
if err := chromedp.Run(d.ctx,
|
|
chromedp.CaptureScreenshot(&buf),
|
|
); err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
img, err := png.Decode(bytes.NewReader(buf))
|
|
if err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
return img
|
|
}
|
|
|
|
func (d *JSTestDriver) Click(x, y int) {
|
|
if err := chromedp.Run(d.ctx,
|
|
chromedp.MouseClickXY(float64(x), float64(y)),
|
|
); err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
// TODO(mvdan): synchronize with the app instead
|
|
time.Sleep(200 * time.Millisecond)
|
|
}
|
|
|
|
func TestJS(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
runEndToEndTest(t, &JSTestDriver{})
|
|
}
|