mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-03 16:35:36 +00:00
726270ab2d
The js/wasm bug meant that printing to stdout in the e2e app would crash the runtime, so we couldn't synchronize with the app like in the other drivers. Now that the bug is fixed, we can do the same as the other drivers just fine. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
165 lines
4.0 KiB
Go
165 lines
4.0 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"
|
|
|
|
"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
|
|
|
|
frameNotifs chan bool
|
|
|
|
// ctx is the chromedp context.
|
|
ctx context.Context
|
|
}
|
|
|
|
func (d *JSTestDriver) Start(t_ *testing.T, path string, width, height int) {
|
|
d.frameNotifs = make(chan bool, 1)
|
|
d.t = t_
|
|
|
|
if raceEnabled {
|
|
d.t.Skipf("js/wasm doesn't support -race; skipping")
|
|
}
|
|
|
|
// First, build the app.
|
|
dir, err := ioutil.TempDir("", "gio-endtoend-js")
|
|
if err != nil {
|
|
d.t.Fatal(err)
|
|
}
|
|
d.t.Cleanup(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...)
|
|
d.t.Cleanup(cancel)
|
|
|
|
ctx, cancel := chromedp.NewContext(actx,
|
|
// Send all logf/errf calls to t.Logf
|
|
chromedp.WithLogf(d.t.Logf),
|
|
)
|
|
d.t.Cleanup(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:
|
|
if ev.Type == "log" && len(ev.Args) == 1 &&
|
|
// Note that the argument values are JSON.
|
|
string(ev.Args[0].Value) == `"frame ready"` {
|
|
|
|
d.frameNotifs <- true
|
|
// These logs are expected. Don't show them.
|
|
break
|
|
}
|
|
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)))
|
|
d.t.Cleanup(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)
|
|
}
|
|
|
|
// Wait for the gio app to render.
|
|
<-d.frameNotifs
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
// Wait for the gio app to render after this click.
|
|
<-d.frameNotifs
|
|
}
|