js_test.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package main_test
   4  
   5  import (
   6  	"bytes"
   7  	"context"
   8  	"errors"
   9  	"image"
  10  	"image/png"
  11  	"io"
  12  	"net/http"
  13  	"net/http/httptest"
  14  	"os/exec"
  15  
  16  	"github.com/chromedp/cdproto/runtime"
  17  	"github.com/chromedp/chromedp"
  18  
  19  )
  20  
  21  type JSTestDriver struct {
  22  	driverBase
  23  
  24  	// ctx is the chromedp context.
  25  	ctx context.Context
  26  }
  27  
  28  func (d *JSTestDriver) Start(path string) {
  29  	if raceEnabled {
  30  		d.Skipf("js/wasm doesn't support -race; skipping")
  31  	}
  32  
  33  	// First, build the app.
  34  	dir := d.tempDir("gio-endtoend-js")
  35  	d.gogio("-target=js", "-o="+dir, path)
  36  
  37  	// Second, start Chrome.
  38  	opts := append(chromedp.DefaultExecAllocatorOptions[:],
  39  		chromedp.Flag("headless", *headless),
  40  	)
  41  
  42  	actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
  43  	d.Cleanup(cancel)
  44  
  45  	ctx, cancel := chromedp.NewContext(actx,
  46  		// Send all logf/errf calls to t.Logf
  47  		chromedp.WithLogf(d.Logf),
  48  	)
  49  	d.Cleanup(cancel)
  50  	d.ctx = ctx
  51  
  52  	if err := chromedp.Run(ctx); err != nil {
  53  		if errors.Is(err, exec.ErrNotFound) {
  54  			d.Skipf("test requires Chrome to be installed: %v", err)
  55  			return
  56  		}
  57  		d.Fatal(err)
  58  	}
  59  	pr, pw := io.Pipe()
  60  	d.Cleanup(func() { pw.Close() })
  61  	d.output = pr
  62  	chromedp.ListenTarget(ctx, func(ev interface{}) {
  63  		switch ev := ev.(type) {
  64  		case *runtime.EventConsoleAPICalled:
  65  			switch ev.Type {
  66  			case "log", "info", "warning", "error":
  67  				var b bytes.Buffer
  68  				b.WriteString("console.")
  69  				b.WriteString(string(ev.Type))
  70  				b.WriteString("(")
  71  				for i, arg := range ev.Args {
  72  					if i > 0 {
  73  						b.WriteString(", ")
  74  					}
  75  					b.Write(arg.Value)
  76  				}
  77  				b.WriteString(")\n")
  78  				pw.Write(b.Bytes())
  79  			}
  80  		}
  81  	})
  82  
  83  	// Third, serve the app folder, set the browser tab dimensions, and
  84  	// navigate to the folder.
  85  	ts := httptest.NewServer(http.FileServer(http.Dir(dir)))
  86  	d.Cleanup(ts.Close)
  87  
  88  	if err := chromedp.Run(ctx,
  89  		chromedp.EmulateViewport(int64(d.width), int64(d.height)),
  90  		chromedp.Navigate(ts.URL),
  91  	); err != nil {
  92  		d.Fatal(err)
  93  	}
  94  
  95  	// Wait for the gio app to render.
  96  	d.waitForFrame()
  97  }
  98  
  99  func (d *JSTestDriver) Screenshot() image.Image {
 100  	var buf []byte
 101  	if err := chromedp.Run(d.ctx,
 102  		chromedp.CaptureScreenshot(&buf),
 103  	); err != nil {
 104  		d.Fatal(err)
 105  	}
 106  	img, err := png.Decode(bytes.NewReader(buf))
 107  	if err != nil {
 108  		d.Fatal(err)
 109  	}
 110  	return img
 111  }
 112  
 113  func (d *JSTestDriver) Click(x, y int) {
 114  	if err := chromedp.Run(d.ctx,
 115  		chromedp.MouseClickXY(float64(x), float64(y)),
 116  	); err != nil {
 117  		d.Fatal(err)
 118  	}
 119  
 120  	// Wait for the gio app to render after this click.
 121  	d.waitForFrame()
 122  }
 123