From dc4a0043fc4c56aabe5e25b32174a363244563ea Mon Sep 17 00:00:00 2001 From: Yad Smood Date: Wed, 30 Mar 2022 09:26:19 +0800 Subject: [PATCH] update got version Here's the code we use to refactor ```go func main() { _ = filepath.Walk(".", func(path string, info fs.FileInfo, err error) error { if !strings.HasSuffix(path, "_test.go") || strings.HasPrefix(path, "lib/proto/") { return nil } fmt.Println(path) f, _ := os.ReadFile(path) ms := regexp.MustCompile(`(?m)^func \(t T\)`).FindAllIndex(f, -1) for _, m := range ms { cmd := fmt.Sprintf("gopls -remote=127.0.0.1:8888 rename -w %s:#%d g", path, m[0]+6) utils.ExecLine(cmd) } f, _ = os.ReadFile(path) f = regexp.MustCompile(`(?m)^func \(g T\) ([^\s]+)\(\) \{`). ReplaceAll(f, []byte("func Test$1(t *testing.T) {\n\tg := setup(t)\n")) err = os.WriteFile(path, f, info.Mode()) return err }) } ``` --- .github/CONTRIBUTING.md | 12 +- browser_test.go | 319 +++++++------ dev_helpers_test.go | 83 ++-- element_test.go | 765 +++++++++++++++++------------- go.mod | 6 +- go.sum | 12 +- hijack_test.go | 155 +++--- lib/cdp/client_private_test.go | 98 ++-- lib/cdp/client_test.go | 78 +-- lib/cdp/websocket_private_test.go | 19 +- lib/cdp/websocket_test.go | 52 +- lib/js/helper.js | 2 +- lib/launcher/launcher_test.go | 104 ++-- lib/launcher/private_test.go | 100 ++-- lib/utils/sleeper_test.go | 53 ++- lib/utils/utils_test.go | 146 +++--- must_test.go | 68 +-- page_eval_test.go | 211 ++++---- page_test.go | 661 +++++++++++++++----------- query_test.go | 403 +++++++++------- setup_test.go | 128 ++--- 21 files changed, 1980 insertions(+), 1495 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 885624ae..1e668a1d 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -40,18 +40,10 @@ The `cdp` requests of each test will be recorded and output to folder `tmp/cdp-l [artifacts](https://docs.github.com/en/actions/guides/storing-workflow-data-as-artifacts) so that we can download them for debugging. -### Filter tests - -Use regex: `go test -run /^Click$`. - -Test a specific package: `go test ./lib/launcher`. - -Run all tests: `go test ./...` - ### Disable headless mode ```bash -rod=show,trace,slow=2s go test -run /Click +rod=show,trace,slow=2s go test ``` Check type `defaults.ResetWithEnv` for how it works. @@ -98,7 +90,7 @@ There are several helper functions for it: 1. Run lint in the container: `go run ./lib/utils/lint` -1. Run tests in the container: `go test -run /Click` +1. Run tests in the container: `go test` 1. After you exit the container with `exit`, you can restart it by: `docker start -i rod` diff --git a/browser_test.go b/browser_test.go index 7ea0faf5..968347d4 100644 --- a/browser_test.go +++ b/browser_test.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "runtime" + "testing" "time" "github.com/go-rod/rod" @@ -20,110 +21,124 @@ import ( "github.com/ysmood/gson" ) -func (t T) Incognito() { - k := t.Srand(16) +func TestIncognito(t *testing.T) { + g := setup(t) - b := t.browser.MustIncognito().Sleeper(rod.DefaultSleeper) + k := g.RandStr(16) + + b := g.browser.MustIncognito().Sleeper(rod.DefaultSleeper) defer b.MustClose() - page := b.MustPage(t.blank()) + page := b.MustPage(g.blank()) defer page.MustClose() page.MustEval(`k => localStorage[k] = 1`, k) - t.True(t.page.MustNavigate(t.blank()).MustEval(`k => localStorage[k]`, k).Nil()) - t.Eq(page.MustEval(`k => localStorage[k]`, k).Str(), "1") // localStorage can only store string + g.True(g.page.MustNavigate(g.blank()).MustEval(`k => localStorage[k]`, k).Nil()) + g.Eq(page.MustEval(`k => localStorage[k]`, k).Str(), "1") // localStorage can only store string } -func (t T) BrowserResetControlURL() { +func TestBrowserResetControlURL(t *testing.T) { rod.New().ControlURL("test").ControlURL("") } -func (t T) DefaultDevice() { +func TestDefaultDevice(t *testing.T) { + g := setup(t) + ua := "" - s := t.Serve() + s := g.Serve() s.Mux.HandleFunc("/t", func(rw http.ResponseWriter, r *http.Request) { ua = r.Header.Get("User-Agent") }) - t.browser.DefaultDevice(devices.IPhoneX) - defer t.browser.DefaultDevice(devices.LaptopWithMDPIScreen.Landescape()) + g.browser.DefaultDevice(devices.IPhoneX) + defer g.browser.DefaultDevice(devices.LaptopWithMDPIScreen.Landescape()) - t.newPage(s.URL("/t")) - t.Eq(ua, devices.IPhoneX.UserAgentEmulation().UserAgent) + g.newPage(s.URL("/t")) + g.Eq(ua, devices.IPhoneX.UserAgentEmulation().UserAgent) - t.browser.NoDefaultDevice() - t.newPage(s.URL("/t")) - t.Neq(ua, devices.IPhoneX.UserAgentEmulation().UserAgent) + g.browser.NoDefaultDevice() + g.newPage(s.URL("/t")) + g.Neq(ua, devices.IPhoneX.UserAgentEmulation().UserAgent) } -func (t T) PageErr() { - t.Panic(func() { - t.mc.stubErr(1, proto.TargetAttachToTarget{}) - t.browser.MustPage() +func TestPageErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { + g.mc.stubErr(1, proto.TargetAttachToTarget{}) + g.browser.MustPage() }) } -func (t T) PageFromTarget() { - t.Panic(func() { - res, err := proto.TargetCreateTarget{URL: "about:blank"}.Call(t.browser) - t.E(err) +func TestPageFromTarget(t *testing.T) { + g := setup(t) + + g.Panic(func() { + res, err := proto.TargetCreateTarget{URL: "about:blank"}.Call(g.browser) + g.E(err) defer func() { - t.browser.MustPageFromTargetID(res.TargetID).MustClose() + g.browser.MustPageFromTargetID(res.TargetID).MustClose() }() - t.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) - t.browser.MustPageFromTargetID(res.TargetID) + g.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) + g.browser.MustPageFromTargetID(res.TargetID) }) } -func (t T) BrowserPages() { - t.newPage(t.blank()).MustWaitLoad() +func TestBrowserPages(t *testing.T) { + g := setup(t) + + g.newPage(g.blank()).MustWaitLoad() - pages := t.browser.MustPages() + pages := g.browser.MustPages() - t.Len(pages, 3) + g.Len(pages, 3) { - t.mc.stub(1, proto.TargetGetTargets{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.TargetGetTargets{}, func(send StubSend) (gson.JSON, error) { d, _ := send() return *d.Set("targetInfos.0.type", "iframe"), nil }) - pages := t.browser.MustPages() - t.Len(pages, 2) + pages := g.browser.MustPages() + g.Len(pages, 2) } - t.Panic(func() { - t.mc.stubErr(1, proto.TargetCreateTarget{}) - t.browser.MustPage() + g.Panic(func() { + g.mc.stubErr(1, proto.TargetCreateTarget{}) + g.browser.MustPage() }) - t.Panic(func() { - t.mc.stubErr(1, proto.TargetGetTargets{}) - t.browser.MustPages() + g.Panic(func() { + g.mc.stubErr(1, proto.TargetGetTargets{}) + g.browser.MustPages() }) - t.Panic(func() { - res, err := proto.TargetCreateTarget{URL: "about:blank"}.Call(t.browser) - t.E(err) + g.Panic(func() { + res, err := proto.TargetCreateTarget{URL: "about:blank"}.Call(g.browser) + g.E(err) defer func() { - t.browser.MustPageFromTargetID(res.TargetID).MustClose() + g.browser.MustPageFromTargetID(res.TargetID).MustClose() }() - t.mc.stubErr(1, proto.TargetAttachToTarget{}) - t.browser.MustPages() + g.mc.stubErr(1, proto.TargetAttachToTarget{}) + g.browser.MustPages() }) } -func (t T) BrowserClearStates() { - t.E(proto.EmulationClearGeolocationOverride{}.Call(t.page)) +func TestBrowserClearStates(t *testing.T) { + g := setup(t) + + g.E(proto.EmulationClearGeolocationOverride{}.Call(g.page)) } -func (t T) BrowserEvent() { - messages := t.browser.Context(t.Context()).Event() - p := t.newPage() +func TestBrowserEvent(t *testing.T) { + g := setup(t) + + messages := g.browser.Context(g.Context()).Event() + p := g.newPage() wait := make(chan struct{}) for msg := range messages { e := proto.TargetAttachedToTarget{} if msg.Load(&e) { - t.Eq(e.TargetInfo.TargetID, p.TargetID) + g.Eq(e.TargetInfo.TargetID, p.TargetID) close(wait) break } @@ -131,7 +146,7 @@ func (t T) BrowserEvent() { <-wait } -func (t T) BrowserEventClose() { +func TestBrowserEventClose(t *testing.T) { event := make(chan *cdp.Event) c := &MockClient{ connect: func() error { return nil }, @@ -146,26 +161,30 @@ func (t T) BrowserEventClose() { close(event) } -func (t T) BrowserWaitEvent() { - t.NotNil(t.browser.Context(t.Context()).Event()) +func TestBrowserWaitEvent(t *testing.T) { + g := setup(t) + + g.NotNil(g.browser.Context(g.Context()).Event()) - wait := t.page.WaitEvent(proto.PageFrameNavigated{}) - t.page.MustNavigate(t.blank()) + wait := g.page.WaitEvent(proto.PageFrameNavigated{}) + g.page.MustNavigate(g.blank()) wait() - wait = t.browser.EachEvent(func(e *proto.PageFrameNavigated, id proto.TargetSessionID) bool { + wait = g.browser.EachEvent(func(e *proto.PageFrameNavigated, id proto.TargetSessionID) bool { return true }) - t.page.MustNavigate(t.blank()) + g.page.MustNavigate(g.blank()) wait() } -func (t T) BrowserCrash() { - browser := rod.New().Context(t.Context()).MustConnect() +func TestBrowserCrash(t *testing.T) { + g := setup(t) + + browser := rod.New().Context(g.Context()).MustConnect() page := browser.MustPage() js := `() => new Promise(r => setTimeout(r, 10000))` - go t.Panic(func() { + go g.Panic(func() { page.MustEval(js) }) @@ -176,88 +195,100 @@ func (t T) BrowserCrash() { utils.Sleep(0.3) _, err := page.Eval(js) - t.Is(err, cdp.ErrConnClosed) + g.Is(err, cdp.ErrConnClosed) } -func (t T) BrowserCall() { - v, err := proto.BrowserGetVersion{}.Call(t.browser) - t.E(err) +func TestBrowserCall(t *testing.T) { + g := setup(t) - t.Regex("1.3", v.ProtocolVersion) + v, err := proto.BrowserGetVersion{}.Call(g.browser) + g.E(err) + + g.Regex("1.3", v.ProtocolVersion) } -func (t T) BlockingNavigation() { +func TestBlockingNavigation(t *testing.T) { + g := setup(t) + /* Navigate can take forever if a page doesn't response. If one page is blocked, other pages should still work. */ - s := t.Serve() - pause := t.Context() + s := g.Serve() + pause := g.Context() s.Mux.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) { <-pause.Done() }) s.Route("/b", ".html", `ok`) - blocked := t.newPage() + blocked := g.newPage() go func() { - t.Panic(func() { + g.Panic(func() { blocked.MustNavigate(s.URL("/a")) }) }() utils.Sleep(0.3) - t.newPage(s.URL("/b")) + g.newPage(s.URL("/b")) } -func (t T) ResolveBlocking() { - s := t.Serve() +func TestResolveBlocking(t *testing.T) { + g := setup(t) - pause := t.Context() + s := g.Serve() + + pause := g.Context() s.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { <-pause.Done() }) - p := t.newPage() + p := g.newPage() go func() { utils.Sleep(0.1) p.MustStopLoading() }() - t.Panic(func() { + g.Panic(func() { p.MustNavigate(s.URL()) }) } -func (t T) TestTry() { - t.Nil(rod.Try(func() {})) +func TestTestTry(t *testing.T) { + g := setup(t) + + g.Nil(rod.Try(func() {})) err := rod.Try(func() { panic(1) }) var errVal *rod.ErrTry - t.True(errors.As(err, &errVal)) - t.Is(err, &rod.ErrTry{}) - t.Eq(1, errVal.Value) - t.Eq(errVal.Error(), "error value: 1") + g.True(errors.As(err, &errVal)) + g.Is(err, &rod.ErrTry{}) + g.Eq(1, errVal.Value) + g.Eq(errVal.Error(), "error value: 1") } -func (t T) BrowserOthers() { - t.browser.Timeout(time.Second).CancelTimeout().MustGetCookies() +func TestBrowserOthers(t *testing.T) { + g := setup(t) + + g.browser.Timeout(time.Second).CancelTimeout().MustGetCookies() - t.Panic(func() { - b, cancel := t.browser.WithCancel() + g.Panic(func() { + b, cancel := g.browser.WithCancel() cancel() b.MustIncognito() }) } -func (t T) BinarySize() { +func TestBinarySize(t *testing.T) { + g := setup(t) + if runtime.GOOS == "windows" { - t.SkipNow() + g.SkipNow() } cmd := exec.Command("go", "build", @@ -268,16 +299,18 @@ func (t T) BinarySize() { cmd.Env = append(os.Environ(), "GOOS=linux") - t.Nil(cmd.Run()) + g.Nil(cmd.Run()) stat, err := os.Stat("tmp/translator") - t.E(err) + g.E(err) - t.Lte(float64(stat.Size())/1024/1024, 9.3) // mb + g.Lte(float64(stat.Size())/1024/1024, 9.3) // mb } -func (t T) BrowserCookies() { - b := t.browser.MustIncognito() +func TestBrowserCookies(t *testing.T) { + g := setup(t) + + b := g.browser.MustIncognito() defer b.MustClose() b.MustSetCookies(&proto.NetworkCookie{ @@ -288,38 +321,42 @@ func (t T) BrowserCookies() { cookies := b.MustGetCookies() - t.Len(cookies, 1) - t.Eq(cookies[0].Name, "a") - t.Eq(cookies[0].Value, "val") + g.Len(cookies, 1) + g.Eq(cookies[0].Name, "a") + g.Eq(cookies[0].Value, "val") { b.MustSetCookies() cookies := b.MustGetCookies() - t.Len(cookies, 0) + g.Len(cookies, 0) } - t.mc.stubErr(1, proto.StorageGetCookies{}) - t.Err(b.GetCookies()) + g.mc.stubErr(1, proto.StorageGetCookies{}) + g.Err(b.GetCookies()) } -func (t T) WaitDownload() { - s := t.Serve() +func TestWaitDownload(t *testing.T) { + g := setup(t) + + s := g.Serve() content := "test content" s.Route("/d", ".bin", []byte(content)) s.Route("/page", ".html", fmt.Sprintf(`click`, s.URL())) - page := t.page.MustNavigate(s.URL("/page")) + page := g.page.MustNavigate(s.URL("/page")) - wait := t.browser.MustWaitDownload() + wait := g.browser.MustWaitDownload() page.MustElement("a").MustClick() data := wait() - t.Eq(content, string(data)) + g.Eq(content, string(data)) } -func (t T) WaitDownloadDataURI() { - s := t.Serve() +func TestWaitDownloadDataURI(t *testing.T) { + g := setup(t) + + s := g.Serve() s.Route("/", ".html", ` @@ -334,21 +371,23 @@ func (t T) WaitDownloadDataURI() { `, ) - page := t.page.MustNavigate(s.URL()) + page := g.page.MustNavigate(s.URL()) - wait1 := t.browser.MustWaitDownload() + wait1 := g.browser.MustWaitDownload() page.MustElement("#a").MustClick() data := wait1() - t.Eq("test data", string(data)) + g.Eq("test data", string(data)) - wait2 := t.browser.MustWaitDownload() + wait2 := g.browser.MustWaitDownload() page.MustElement("#b").MustClick() data = wait2() - t.Eq("test blob", string(data)) + g.Eq("test blob", string(data)) } -func (t T) WaitDownloadFromNewPage() { - s := t.Serve() +func TestWaitDownloadFromNewPage(t *testing.T) { + g := setup(t) + + s := g.Serve() content := "test content" s.Route("/d", ".bin", content) @@ -357,20 +396,22 @@ func (t T) WaitDownloadFromNewPage() { s.URL()), ) - page := t.page.MustNavigate(s.URL("/page")) - wait := t.browser.MustWaitDownload() + page := g.page.MustNavigate(s.URL("/page")) + wait := g.browser.MustWaitDownload() page.MustElement("a").MustClick() data := wait() - t.Eq(content, string(data)) + g.Eq(content, string(data)) } -func (t T) BrowserConnectErr() { - t.Panic(func() { +func TestBrowserConnectErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { c := &MockClient{connect: func() error { return errors.New("err") }} rod.New().Client(c).MustConnect() }) - t.Panic(func() { + g.Panic(func() { ch := make(chan *cdp.Event) defer close(ch) @@ -380,42 +421,46 @@ func (t T) BrowserConnectErr() { }) } -func (t T) StreamReader() { - r := rod.NewStreamReader(t.page, "") +func TestStreamReader(t *testing.T) { + g := setup(t) - t.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { + r := rod.NewStreamReader(g.page, "") + + g.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { return gson.New(proto.IOReadResult{ Data: "test", }), nil }) b := make([]byte, 4) _, _ = r.Read(b) - t.Eq("test", string(b)) + g.Eq("test", string(b)) - t.mc.stubErr(1, proto.IORead{}) + g.mc.stubErr(1, proto.IORead{}) _, err := r.Read(nil) - t.Err(err) + g.Err(err) - t.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { return gson.New(proto.IOReadResult{ Base64Encoded: true, Data: "@", }), nil }) _, err = r.Read(nil) - t.Err(err) + g.Err(err) } -func (t T) BrowserConnectFailure() { - c := t.Context() +func TestBrowserConnectFailure(t *testing.T) { + g := setup(t) + + c := g.Context() c.Cancel() err := rod.New().Context(c).Connect() if err == nil { - t.Fatal("expected an error on connect failure") + g.Fatal("expected an error on connect failure") } } -func (t T) BrowserPool() { +func TestBrowserPool(t *testing.T) { pool := rod.NewBrowserPool(3) create := func() *rod.Browser { return rod.New().MustConnect() } b := pool.Get(create) @@ -425,11 +470,11 @@ func (t T) BrowserPool() { }) } -func (t T) OldBrowser(got.Skip) { +func (g G) OldBrowser(got.Skip) { u := launcher.New().Revision(686378).MustLaunch() b := rod.New().ControlURL(u).MustConnect() - t.Cleanup(b.MustClose) + g.Cleanup(b.MustClose) res, err := proto.BrowserGetVersion{}.Call(b) - t.E(err) - t.Eq(res.Revision, "@19d4547535ab5aba70b4730443f84e8153052174") + g.E(err) + g.Eq(res.Revision, "@19d4547535ab5aba70b4730443f84e8153052174") } diff --git a/dev_helpers_test.go b/dev_helpers_test.go index 43ae8cc4..e163e337 100644 --- a/dev_helpers_test.go +++ b/dev_helpers_test.go @@ -1,6 +1,7 @@ package rod_test import ( + "testing" "time" "github.com/go-rod/rod" @@ -12,86 +13,96 @@ import ( "github.com/ysmood/gson" ) -func (t T) Monitor() { +func TestMonitor(t *testing.T) { + g := setup(t) + b := rod.New().MustConnect() defer b.MustClose() - p := b.MustPage(t.blank()).MustWaitLoad() + p := b.MustPage(g.blank()).MustWaitLoad() b, cancel := b.WithCancel() defer cancel() - host := b.Context(t.Context()).ServeMonitor("") + host := b.Context(g.Context()).ServeMonitor("") - page := t.page.MustNavigate(host) - t.Has(page.MustElement("#targets a").MustParent().MustHTML(), string(p.TargetID)) + page := g.page.MustNavigate(host) + g.Has(page.MustElement("#targets a").MustParent().MustHTML(), string(p.TargetID)) page.MustNavigate(host + "/page/" + string(p.TargetID)) page.MustWait(`(id) => document.title.includes(id)`, p.TargetID) - img := t.Req("", host+"/screenshot").Bytes() - t.Gt(img.Len(), 10) + img := g.Req("", host+"/screenshot").Bytes() + g.Gt(img.Len(), 10) - res := t.Req("", host+"/api/page/test") - t.Eq(400, res.StatusCode) - t.Eq(-32602, gson.New(res.Body).Get("code").Int()) + res := g.Req("", host+"/api/page/test") + g.Eq(400, res.StatusCode) + g.Eq(-32602, gson.New(res.Body).Get("code").Int()) } -func (t T) MonitorErr() { +func TestMonitorErr(t *testing.T) { + g := setup(t) + l := launcher.New() u := l.MustLaunch() defer l.Kill() - t.Panic(func() { + g.Panic(func() { rod.New().Monitor("abc").ControlURL(u).MustConnect() }) } -func (t T) Trace() { - t.Eq(rod.TraceTypeInput.String(), "[input]") +func TestTrace(t *testing.T) { + g := setup(t) + + g.Eq(rod.TraceTypeInput.String(), "[input]") var msg []interface{} - t.browser.Logger(utils.Log(func(list ...interface{}) { msg = list })) - t.browser.Trace(true).SlowMotion(time.Microsecond) + g.browser.Logger(utils.Log(func(list ...interface{}) { msg = list })) + g.browser.Trace(true).SlowMotion(time.Microsecond) defer func() { - t.browser.Logger(rod.DefaultLogger) - t.browser.Trace(defaults.Trace).SlowMotion(defaults.Slow) + g.browser.Logger(rod.DefaultLogger) + g.browser.Trace(defaults.Trace).SlowMotion(defaults.Slow) }() - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")).MustWaitLoad() + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")).MustWaitLoad() - t.Eq(rod.TraceTypeWait, msg[0]) - t.Eq("load", msg[1]) - t.Eq(p, msg[2]) + g.Eq(rod.TraceTypeWait, msg[0]) + g.Eq("load", msg[1]) + g.Eq(p, msg[2]) el := p.MustElement("button") el.MustClick() - t.Eq(rod.TraceTypeInput, msg[0]) - t.Eq("left click", msg[1]) - t.Eq(el, msg[2]) + g.Eq(rod.TraceTypeInput, msg[0]) + g.Eq("left click", msg[1]) + g.Eq(el, msg[2]) - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) _ = p.Mouse.Move(10, 10, 1) } -func (t T) TraceLogs() { - t.browser.Logger(utils.LoggerQuiet) - t.browser.Trace(true) +func TestTraceLogs(t *testing.T) { + g := setup(t) + + g.browser.Logger(utils.LoggerQuiet) + g.browser.Trace(true) defer func() { - t.browser.Logger(rod.DefaultLogger) - t.browser.Trace(defaults.Trace) + g.browser.Logger(rod.DefaultLogger) + g.browser.Trace(defaults.Trace) }() - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") el.MustClick() - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) p.Overlay(0, 0, 100, 30, "") } -func (t T) ExposeHelpers() { - p := t.newPage(t.srcFile("fixtures/click.html")) +func TestExposeHelpers(t *testing.T) { + g := setup(t) + + p := g.newPage(g.srcFile("fixtures/click.html")) p.ExposeHelpers(js.ElementR) - t.Eq(p.MustElementByJS(`() => rod.elementR('button', 'click me')`).MustText(), "click me") + g.Eq(p.MustElementByJS(`() => rod.elementR('button', 'click me')`).MustText(), "click me") } diff --git a/element_test.go b/element_test.go index e860f882..cecf2a7b 100644 --- a/element_test.go +++ b/element_test.go @@ -8,6 +8,7 @@ import ( "image/png" "os" "path/filepath" + "testing" "time" "github.com/go-rod/rod" @@ -19,80 +20,92 @@ import ( "github.com/ysmood/gson" ) -func (t T) GetElementPage() { - el := t.page.MustNavigate(t.blank()).MustElement("html") - t.Eq(el.Page().SessionID, t.page.SessionID) +func TestGetElementPage(t *testing.T) { + g := setup(t) + + el := g.page.MustNavigate(g.blank()).MustElement("html") + g.Eq(el.Page().SessionID, g.page.SessionID) } -func (t T) Click() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestClick(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") el.MustClick() - t.True(p.MustHas("[a=ok]")) + g.True(p.MustHas("[a=ok]")) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustClick() }) - t.Panic(func() { - t.mc.stubErr(8, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(8, proto.RuntimeCallFunctionOn{}) el.MustClick() }) } -func (t T) ClickWrapped() { - p := t.page.MustNavigate(t.srcFile("fixtures/click-wrapped.html")).MustWaitLoad() +func TestClickWrapped(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click-wrapped.html")).MustWaitLoad() el := p.MustElement("#target") shape := el.MustShape() - t.Len(shape.Quads, 2) + g.Len(shape.Quads, 2) el.MustClick() - t.True(p.MustHas("[a=ok]")) + g.True(p.MustHas("[a=ok]")) } -func (t T) Tap() { - page := t.newPage() +func TestTap(t *testing.T) { + g := setup(t) + + page := g.newPage() page.MustEmulate(devices.IPad). - MustNavigate(t.srcFile("fixtures/touch.html")). + MustNavigate(g.srcFile("fixtures/touch.html")). MustWaitLoad() el := page.MustElement("button") el.MustTap() - t.True(page.MustHas("[tapped=true]")) + g.True(page.MustHas("[tapped=true]")) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustTap() }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustTap() }) - t.Panic(func() { - t.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) el.MustTap() }) - t.Panic(func() { - t.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) el.MustTap() }) } -func (t T) Interactable() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestInteractable(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") - t.True(el.MustInteractable()) + g.True(el.MustInteractable()) - t.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) - t.Err(el.Interactable()) + g.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) + g.Err(el.Interactable()) } -func (t T) NotInteractable() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestNotInteractable(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") // cover the button with a green div @@ -102,58 +115,64 @@ func (t T) NotInteractable() { document.body.append(div) }`) _, err := el.Interactable() - t.Has(err.Error(), "element covered by:
") - t.Is(err, &rod.ErrNotInteractable{}) - t.Is(err, &rod.ErrCovered{}) - t.False(el.MustInteractable()) + g.Has(err.Error(), "element covered by:
") + g.Is(err, &rod.ErrNotInteractable{}) + g.Is(err, &rod.ErrCovered{}) + g.False(el.MustInteractable()) var ee *rod.ErrNotInteractable - t.True(errors.As(err, &ee)) - t.Eq(ee.Error(), "element is not cursor interactable") + g.True(errors.As(err, &ee)) + g.Eq(ee.Error(), "element is not cursor interactable") p.MustElement("div").MustRemove() - t.mc.stubErr(1, proto.DOMGetContentQuads{}) + g.mc.stubErr(1, proto.DOMGetContentQuads{}) _, err = el.Interactable() - t.Err(err) + g.Err(err) - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(el.Interactable()) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(el.Interactable()) - t.mc.stubErr(1, proto.DOMDescribeNode{}) - t.Err(el.Interactable()) + g.mc.stubErr(1, proto.DOMDescribeNode{}) + g.Err(el.Interactable()) - t.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) - t.Err(el.Interactable()) + g.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) + g.Err(el.Interactable()) } -func (t T) InteractableWithNoShape() { - p := t.page.MustNavigate(t.srcFile("fixtures/interactable.html")) +func TestInteractableWithNoShape(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/interactable.html")) el := p.MustElement("#no-shape") _, err := el.Interactable() - t.Is(err, &rod.ErrInvisibleShape{}) - t.Is(err, &rod.ErrNotInteractable{}) - t.Eq(err.Error(), "element has no visible shape or outside the viewport: ") + g.Is(err, &rod.ErrInvisibleShape{}) + g.Is(err, &rod.ErrNotInteractable{}) + g.Eq(err.Error(), "element has no visible shape or outside the viewport: ") el = p.MustElement("#outside") _, err = el.Interactable() - t.Is(err, &rod.ErrInvisibleShape{}) + g.Is(err, &rod.ErrInvisibleShape{}) el = p.MustElement("#invisible") _, err = el.Interactable() - t.Is(err, &rod.ErrInvisibleShape{}) + g.Is(err, &rod.ErrInvisibleShape{}) } -func (t T) NotInteractableWithNoPointerEvents() { - p := t.page.MustNavigate(t.srcFile("fixtures/interactable.html")) +func TestNotInteractableWithNoPointerEvents(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/interactable.html")) _, err := p.MustElementR("#no-pointer-events", "click me").Interactable() - t.Is(err, &rod.ErrNoPointerEvents{}) - t.Is(err, &rod.ErrNotInteractable{}) - t.Eq(err.Error(), "element's pointer-events is none: ") + g.Is(err, &rod.ErrNoPointerEvents{}) + g.Is(err, &rod.ErrNotInteractable{}) + g.Eq(err.Error(), "element's pointer-events is none: ") } -func (t T) WaitInteractable() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestWaitInteractable(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") start := time.Now() @@ -168,58 +187,68 @@ func (t T) WaitInteractable() { el.MustWaitInteractable() - t.Gt(time.Since(start), time.Second) + g.Gt(time.Since(start), time.Second) - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) - t.Err(el.WaitInteractable()) + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Err(el.WaitInteractable()) } -func (t T) Hover() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestHover(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") el.MustEval(`() => this.onmouseenter = () => this.dataset['a'] = 1`) el.MustHover() - t.Eq("1", el.MustEval(`() => this.dataset['a']`).String()) + g.Eq("1", el.MustEval(`() => this.dataset['a']`).String()) - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) - t.Err(el.Hover()) + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Err(el.Hover()) - t.mc.stubErr(1, proto.DOMGetContentQuads{}) - t.Err(el.Hover()) + g.mc.stubErr(1, proto.DOMGetContentQuads{}) + g.Err(el.Hover()) - t.mc.stubErr(3, proto.DOMGetContentQuads{}) - t.Err(el.Hover()) + g.mc.stubErr(3, proto.DOMGetContentQuads{}) + g.Err(el.Hover()) - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) - t.Err(el.Hover()) + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Err(el.Hover()) } -func (t T) ElementMoveMouseOut() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestElementMoveMouseOut(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) btn := p.MustElement("button") btn.MustEval(`() => this.onmouseout = () => this.setAttribute('name', 'mouse moved.')`) - t.Eq("mouse moved.", *btn.MustHover().MustMoveMouseOut().MustAttribute("name")) + g.Eq("mouse moved.", *btn.MustHover().MustMoveMouseOut().MustAttribute("name")) - t.mc.stubErr(1, proto.DOMGetContentQuads{}) - t.Err(btn.MoveMouseOut()) + g.mc.stubErr(1, proto.DOMGetContentQuads{}) + g.Err(btn.MoveMouseOut()) } -func (t T) MouseMoveErr() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) - t.Err(p.Mouse.Move(10, 10, 1)) +func TestMouseMoveErr(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Err(p.Mouse.Move(10, 10, 1)) } -func (t T) ElementContext() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestElementContext(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button").Timeout(time.Hour).CancelTimeout() el, cancel := el.WithCancel() defer cancel() el.Sleeper(rod.DefaultSleeper).MustClick() } -func (t T) Iframes() { - p := t.page.MustNavigate(t.srcFile("fixtures/click-iframes.html")) +func TestIframes(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click-iframes.html")) frame01 := p.MustElement("iframe").MustFrame() @@ -227,293 +256,325 @@ func (t T) Iframes() { el := frame02.MustElement("button") el.MustClick() - t.Eq(frame01.MustEval(`() => testIsolation()`).Str(), "ok") - t.True(frame02.MustHas("[a=ok]")) + g.Eq(frame01.MustEval(`() => testIsolation()`).Str(), "ok") + g.True(frame02.MustHas("[a=ok]")) } -func (t T) Contains() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestContains(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) a := p.MustElement("button") b := p.MustElementFromNode(a.MustDescribe()) - t.True(a.MustContainsElement(b)) + g.True(a.MustContainsElement(b)) pt := a.MustShape().OnePointInside() el := p.MustElementFromPoint(int(pt.X), int(pt.Y)) - t.True(a.MustContainsElement(el)) + g.True(a.MustContainsElement(el)) - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(a.ContainsElement(el)) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(a.ContainsElement(el)) } -func (t T) ShadowDOM() { - p := t.page.MustNavigate(t.srcFile("fixtures/shadow-dom.html")).MustWaitLoad() +func TestShadowDOM(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/shadow-dom.html")).MustWaitLoad() el := p.MustElement("#container") - t.Eq("inside", el.MustShadowRoot().MustElement("p").MustText()) + g.Eq("inside", el.MustShadowRoot().MustElement("p").MustText()) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMDescribeNode{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMDescribeNode{}) el.MustShadowRoot() }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMResolveNode{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMResolveNode{}) el.MustShadowRoot() }) } -func (t T) Press() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestPress(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("[type=text]") el.MustPress('1', '2', input.Backspace, ' ') el.MustPress([]rune("A b")...) - t.Eq("1 A b", el.MustText()) + g.Eq("1 A b", el.MustText()) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustPress(' ') }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustSelectAllText() }) } -func (t T) KeyDown() { - p := t.page.MustNavigate(t.srcFile("fixtures/keys.html")) +func TestKeyDown(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/keys.html")) p.MustElement("body") p.Keyboard.MustDown('j') - t.True(p.MustHas("body[event=key-down-j]")) + g.True(p.MustHas("body[event=key-down-j]")) } -func (t T) KeyUp() { - p := t.page.MustNavigate(t.srcFile("fixtures/keys.html")) +func TestKeyUp(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/keys.html")) p.MustElement("body") p.Keyboard.MustUp('x') - t.True(p.MustHas("body[event=key-up-x]")) + g.True(p.MustHas("body[event=key-up-x]")) } -func (t T) Input() { +func TestInput(t *testing.T) { + g := setup(t) + text := "雲の上は\nいつも晴れ" - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) { el := p.MustElement("[contenteditable=true]").MustInput(text) - t.Eq(text, el.MustText()) + g.Eq(text, el.MustText()) } el := p.MustElement("textarea") el.MustInput(text) - t.Eq(text, el.MustText()) - t.True(p.MustHas("[event=textarea-change]")) + g.Eq(text, el.MustText()) + g.True(p.MustHas("[event=textarea-change]")) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustText() }) - t.Panic(func() { - t.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) el.MustInput("") }) - t.Panic(func() { - t.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) el.MustInput("") }) - t.Panic(func() { - t.mc.stubErr(6, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(6, proto.RuntimeCallFunctionOn{}) el.MustInput("") }) - t.Panic(func() { - t.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) el.MustInput("") }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputInsertText{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputInsertText{}) el.MustInput("") }) } -func (t T) InputTime() { +func TestInputTime(t *testing.T) { + g := setup(t) + now := time.Now() - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) var el *rod.Element { el = p.MustElement("[type=date]") el.MustInputTime(now) - t.Eq(el.MustText(), now.Format("2006-01-02")) - t.True(p.MustHas("[event=input-date-change]")) + g.Eq(el.MustText(), now.Format("2006-01-02")) + g.True(p.MustHas("[event=input-date-change]")) } { el = p.MustElement("[type=datetime-local]") el.MustInputTime(now) - t.Eq(el.MustText(), now.Format("2006-01-02T15:04")) - t.True(p.MustHas("[event=input-datetime-local-change]")) + g.Eq(el.MustText(), now.Format("2006-01-02T15:04")) + g.True(p.MustHas("[event=input-datetime-local-change]")) } - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustInputTime(now) }) - t.Panic(func() { - t.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) el.MustInputTime(now) }) - t.Panic(func() { - t.mc.stubErr(6, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(6, proto.RuntimeCallFunctionOn{}) el.MustInputTime(now) }) - t.Panic(func() { - t.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(7, proto.RuntimeCallFunctionOn{}) el.MustInputTime(now) }) } -func (t T) Checkbox() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestCheckbox(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("[type=checkbox]") - t.True(el.MustClick().MustProperty("checked").Bool()) + g.True(el.MustClick().MustProperty("checked").Bool()) } -func (t T) SelectText() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestSelectText(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("textarea") el.MustInput("test") el.MustSelectAllText() el.MustInput("test") - t.Eq("test", el.MustText()) + g.Eq("test", el.MustText()) el.MustSelectText(`es`) el.MustInput("__") - t.Eq("t__t", el.MustText()) + g.Eq("t__t", el.MustText()) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustSelectText("") }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustSelectAllText() }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustInput("") }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputInsertText{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputInsertText{}) el.MustInput("") }) } -func (t T) Blur() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestBlur(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("#blur").MustInput("test").MustBlur() - t.Eq("ok", *el.MustAttribute("a")) + g.Eq("ok", *el.MustAttribute("a")) } -func (t T) SelectQuery() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestSelectQuery(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("select") err := el.Select([]string{`[value="c"]`}, true, rod.SelectorTypeCSSSector) - t.E(err) + g.E(err) - t.Eq(2, el.MustEval("() => this.selectedIndex").Int()) + g.Eq(2, el.MustEval("() => this.selectedIndex").Int()) } -func (t T) SelectOptions() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestSelectOptions(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("select") el.MustSelect("B", "C") - t.Eq("B,C", el.MustText()) - t.Eq(1, el.MustProperty("selectedIndex").Int()) + g.Eq("B,C", el.MustText()) + g.Eq(1, el.MustProperty("selectedIndex").Int()) // unselect with regex err := el.Select([]string{`^B$`}, false, rod.SelectorTypeRegex) - t.E(err) - t.Eq("C", el.MustText()) + g.E(err) + g.Eq("C", el.MustText()) // unselect with css selector err = el.Select([]string{`[value="c"]`}, false, rod.SelectorTypeCSSSector) - t.E(err) - t.Eq("", el.MustText()) + g.E(err) + g.Eq("", el.MustText()) // option not found error - t.Is(el.Select([]string{"not-exists"}, true, rod.SelectorTypeCSSSector), &rod.ErrElementNotFound{}) + g.Is(el.Select([]string{"not-exists"}, true, rod.SelectorTypeCSSSector), &rod.ErrElementNotFound{}) { - t.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) - t.Err(el.Select([]string{"B"}, true, rod.SelectorTypeText)) + g.mc.stubErr(5, proto.RuntimeCallFunctionOn{}) + g.Err(el.Select([]string{"B"}, true, rod.SelectorTypeText)) } } -func (t T) Matches() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestMatches(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("textarea") - t.True(el.MustMatches(`[cols="30"]`)) + g.True(el.MustMatches(`[cols="30"]`)) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustMatches("") }) } -func (t T) Attribute() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestAttribute(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("textarea") cols := el.MustAttribute("cols") rows := el.MustAttribute("rows") - t.Eq("30", *cols) - t.Eq("10", *rows) + g.Eq("30", *cols) + g.Eq("10", *rows) - p = t.page.MustNavigate(t.srcFile("fixtures/click.html")) + p = g.page.MustNavigate(g.srcFile("fixtures/click.html")) el = p.MustElement("button").MustClick() - t.Eq("ok", *el.MustAttribute("a")) - t.Nil(el.MustAttribute("b")) + g.Eq("ok", *el.MustAttribute("a")) + g.Nil(el.MustAttribute("b")) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustAttribute("") }) } -func (t T) Property() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestProperty(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("textarea") cols := el.MustProperty("cols") rows := el.MustProperty("rows") - t.Eq(float64(30), cols.Num()) - t.Eq(float64(10), rows.Num()) + g.Eq(float64(30), cols.Num()) + g.Eq(float64(10), rows.Num()) - p = t.page.MustNavigate(t.srcFile("fixtures/open-page.html")) + p = g.page.MustNavigate(g.srcFile("fixtures/open-page.html")) el = p.MustElement("a") - t.Eq("link", el.MustProperty("id").Str()) - t.Eq("_blank", el.MustProperty("target").Str()) - t.True(el.MustProperty("test").Nil()) + g.Eq("link", el.MustProperty("id").Str()) + g.Eq("_blank", el.MustProperty("target").Str()) + g.True(el.MustProperty("test").Nil()) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustProperty("") }) } -func (t T) SetFiles() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestSetFiles(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement(`[type=file]`) el.MustSetFiles( slash("fixtures/click.html"), @@ -521,24 +582,28 @@ func (t T) SetFiles() { ) list := el.MustEval("() => Array.from(this.files).map(f => f.name)").Arr() - t.Len(list, 2) - t.Eq("alert.html", list[1].String()) + g.Len(list, 2) + g.Eq("alert.html", list[1].String()) } -func (t T) Enter() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestEnter(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("[type=submit]") el.MustPress(input.Enter) - t.True(p.MustHas("[event=submit]")) + g.True(p.MustHas("[event=submit]")) } -func (t T) WaitInvisible() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestWaitInvisible(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) h4 := p.MustElement("h4") btn := p.MustElement("button") - t.True(h4.MustVisible()) + g.True(h4.MustVisible()) h4.MustWaitVisible() @@ -552,276 +617,314 @@ func (t T) WaitInvisible() { h4.MustWaitInvisible() btn.MustWaitInvisible() - t.False(p.MustHas("h4")) + g.False(p.MustHas("h4")) } -func (t T) WaitEnabled() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestWaitEnabled(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) p.MustElement("button").MustWaitEnabled() } -func (t T) WaitWritable() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestWaitWritable(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) p.MustElement("input").MustWaitWritable() } -func (t T) WaitStable() { - p := t.page.MustNavigate(t.srcFile("fixtures/wait-stable.html")) +func TestWaitStable(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/wait-stable.html")) el := p.MustElement("button") el.MustEval(`() => this.classList.add("play")`) start := time.Now() el.MustWaitStable() - t.Gt(time.Since(start), time.Second) + g.Gt(time.Since(start), time.Second) - ctx := t.Context() - t.mc.stub(1, proto.DOMGetContentQuads{}, func(send StubSend) (gson.JSON, error) { + ctx := g.Context() + g.mc.stub(1, proto.DOMGetContentQuads{}, func(send StubSend) (gson.JSON, error) { go func() { utils.Sleep(0.1) ctx.Cancel() }() return send() }) - t.Err(el.Context(ctx).WaitStable(time.Minute)) + g.Err(el.Context(ctx).WaitStable(time.Minute)) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMGetContentQuads{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMGetContentQuads{}) el.MustWaitStable() }) - t.Panic(func() { - t.mc.stubErr(2, proto.DOMGetContentQuads{}) + g.Panic(func() { + g.mc.stubErr(2, proto.DOMGetContentQuads{}) el.MustWaitStable() }) } -func (t T) WaitStableRAP() { - p := t.page.MustNavigate(t.srcFile("fixtures/wait-stable.html")) +func TestWaitStableRAP(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/wait-stable.html")) el := p.MustElement("button") el.MustEval(`() => this.classList.add("play")`) start := time.Now() - t.E(el.WaitStableRAF()) - t.Gt(time.Since(start), time.Second) + g.E(el.WaitStableRAF()) + g.Gt(time.Since(start), time.Second) - t.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) - t.Err(el.WaitStableRAF()) + g.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) + g.Err(el.WaitStableRAF()) - t.mc.stubErr(1, proto.DOMGetContentQuads{}) - t.Err(el.WaitStableRAF()) + g.mc.stubErr(1, proto.DOMGetContentQuads{}) + g.Err(el.WaitStableRAF()) } -func (t T) CanvasToImage() { - p := t.page.MustNavigate(t.srcFile("fixtures/canvas.html")) +func TestCanvasToImage(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/canvas.html")) src, err := png.Decode(bytes.NewBuffer(p.MustElement("#canvas").MustCanvasToImage())) - t.E(err) - t.Eq(src.At(50, 50), color.NRGBA{0xFF, 0x00, 0x00, 0xFF}) + g.E(err) + g.Eq(src.At(50, 50), color.NRGBA{0xFF, 0x00, 0x00, 0xFF}) } -func (t T) ElementWaitLoad() { - p := t.page.MustNavigate(t.srcFile("fixtures/resource.html")) +func TestElementWaitLoad(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/resource.html")) p.MustElement("img").MustWaitLoad() } -func (t T) Resource() { - p := t.page.MustNavigate(t.srcFile("fixtures/resource.html")) +func TestResource(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/resource.html")) el := p.MustElement("img") - t.Eq(len(el.MustResource()), 22661) + g.Eq(len(el.MustResource()), 22661) - t.mc.stub(1, proto.PageGetResourceContent{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.PageGetResourceContent{}, func(send StubSend) (gson.JSON, error) { return gson.New(proto.PageGetResourceContentResult{ Content: "ok", Base64Encoded: false, }), nil }) - t.Eq([]byte("ok"), el.MustResource()) + g.Eq([]byte("ok"), el.MustResource()) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustResource() }) - t.Panic(func() { - t.mc.stubErr(1, proto.PageGetResourceContent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageGetResourceContent{}) el.MustResource() }) } -func (t T) BackgroundImage() { - p := t.page.MustNavigate(t.srcFile("fixtures/resource.html")) +func TestBackgroundImage(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/resource.html")) el := p.MustElement("div") - t.Eq(len(el.MustBackgroundImage()), 22661) + g.Eq(len(el.MustBackgroundImage()), 22661) { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(el.BackgroundImage()) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(el.BackgroundImage()) } } -func (t T) ElementScreenshot() { - f := filepath.Join("tmp", "screenshots", t.Srand(16)+".png") - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestElementScreenshot(t *testing.T) { + g := setup(t) + + f := filepath.Join("tmp", "screenshots", g.RandStr(16)+".png") + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("h4") data := el.MustScreenshot(f) img, err := png.Decode(bytes.NewBuffer(data)) - t.E(err) - t.Eq(200, img.Bounds().Dx()) - t.Eq(30, img.Bounds().Dy()) - t.Nil(os.Stat(f)) + g.E(err) + g.Eq(200, img.Bounds().Dx()) + g.Eq(30, img.Bounds().Dy()) + g.Nil(os.Stat(f)) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{}) el.MustScreenshot() }) - t.Panic(func() { - t.mc.stubErr(1, proto.PageCaptureScreenshot{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageCaptureScreenshot{}) el.MustScreenshot() }) - t.Panic(func() { - t.mc.stubErr(3, proto.DOMGetContentQuads{}) + g.Panic(func() { + g.mc.stubErr(3, proto.DOMGetContentQuads{}) el.MustScreenshot() }) } -func (t T) UseReleasedElement() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestUseReleasedElement(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) btn := p.MustElement("button") btn.MustRelease() - t.Err(btn.Click("left")) + g.Err(btn.Click("left")) btn = p.MustElement("button") - t.E(proto.RuntimeReleaseObject{ObjectID: btn.Object.ObjectID}.Call(p)) - t.Is(btn.Click("left"), cdp.ErrObjNotFound) + g.E(proto.RuntimeReleaseObject{ObjectID: btn.Object.ObjectID}.Call(p)) + g.Is(btn.Click("left"), cdp.ErrObjNotFound) } -func (t T) ElementRemove() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestElementRemove(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) btn := p.MustElement("button") - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(btn.Remove()) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(btn.Remove()) } -func (t T) ElementMultipleTimes() { +func TestElementMultipleTimes(t *testing.T) { + g := setup(t) + // To see whether chrome will reuse the remote object ID or not. // Seems like it will not. - page := t.page.MustNavigate(t.srcFile("fixtures/click.html")) + page := g.page.MustNavigate(g.srcFile("fixtures/click.html")) btn01 := page.MustElement("button") btn02 := page.MustElement("button") - t.Eq(btn01.MustText(), btn02.MustText()) - t.Neq(btn01.Object, btn02.Object) + g.Eq(btn01.MustText(), btn02.MustText()) + g.Neq(btn01.Object, btn02.Object) } -func (t T) FnErr() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestFnErr(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") _, err := el.Eval("foo()") - t.Err(err) - t.Has(err.Error(), "ReferenceError: foo is not defined") + g.Err(err) + g.Has(err.Error(), "ReferenceError: foo is not defined") var e *rod.ErrEval - t.True(errors.As(err, &e)) - t.Eq(proto.RuntimeRemoteObjectSubtypeError, e.Exception.Subtype) + g.True(errors.As(err, &e)) + g.Eq(proto.RuntimeRemoteObjectSubtypeError, e.Exception.Subtype) _, err = el.ElementByJS(rod.Eval("() => foo()")) - t.Err(err) - t.Has(err.Error(), "ReferenceError: foo is not defined") - t.True(errors.Is(err, &rod.ErrEval{})) + g.Err(err) + g.Has(err.Error(), "ReferenceError: foo is not defined") + g.True(errors.Is(err, &rod.ErrEval{})) } -func (t T) ElementEWithDepth() { +func TestElementEWithDepth(t *testing.T) { + g := setup(t) + checkStr := `green tea` - p := t.page.MustNavigate(t.srcFile("fixtures/describe.html")) + p := g.page.MustNavigate(g.srcFile("fixtures/describe.html")) ulDOMNode, err := p.MustElement(`ul`).Describe(-1, true) - t.Nil(errors.Unwrap(err)) + g.Nil(errors.Unwrap(err)) data, err := json.Marshal(ulDOMNode) - t.Nil(errors.Unwrap(err)) + g.Nil(errors.Unwrap(err)) // The depth is -1, should contain checkStr - t.Has(string(data), checkStr) + g.Has(string(data), checkStr) } -func (t T) ElementOthers() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestElementOthers(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("form") el.MustFocus() el.MustScrollIntoView() - t.Eq("submit", el.MustElement("[type=submit]").MustText()) - t.Eq("", el.MustElement("[type=submit]").MustHTML()) + g.Eq("submit", el.MustElement("[type=submit]").MustText()) + g.Eq("", el.MustElement("[type=submit]").MustHTML()) el.MustWait(`() => true`) - t.Eq("form", el.MustElementByJS(`() => this`).MustDescribe().LocalName) - t.Len(el.MustElementsByJS(`() => []`), 0) + g.Eq("form", el.MustElementByJS(`() => this`).MustDescribe().LocalName) + g.Len(el.MustElementsByJS(`() => []`), 0) } -func (t T) ElementEqual() { - p := t.page.MustNavigate(t.srcFile("fixtures/describe.html")) +func TestElementEqual(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/describe.html")) el1 := p.MustElement("body > ul") el2 := p.MustElement("html > body > ul") - t.True(el1.MustEqual(el2)) + g.True(el1.MustEqual(el2)) el3 := p.MustElement("ul ul") - t.False(el1.MustEqual(el3)) + g.False(el1.MustEqual(el3)) } -func (t T) ElementFromPointErr() { - t.mc.stubErr(1, proto.DOMGetNodeForLocation{}) - t.Err(t.page.ElementFromPoint(10, 10)) +func TestElementFromPointErr(t *testing.T) { + g := setup(t) + + g.mc.stubErr(1, proto.DOMGetNodeForLocation{}) + g.Err(g.page.ElementFromPoint(10, 10)) } -func (t T) ElementFromNodeErr() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestElementFromNodeErr(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElementX("//button/text()") - t.mc.stubErr(3, proto.RuntimeCallFunctionOn{}) - t.Err(p.ElementFromNode(el.MustDescribe())) + g.mc.stubErr(3, proto.RuntimeCallFunctionOn{}) + g.Err(p.ElementFromNode(el.MustDescribe())) } -func (t T) ElementErrors() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestElementErrors(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("form") - ctx := t.Timeout(0) + ctx := g.Timeout(0) _, err := el.Context(ctx).Describe(-1, true) - t.Err(err) + g.Err(err) _, err = el.Context(ctx).Frame() - t.Err(err) + g.Err(err) err = el.Context(ctx).Focus() - t.Err(err) + g.Err(err) err = el.Context(ctx).Press('a') - t.Err(err) + g.Err(err) err = el.Context(ctx).Input("a") - t.Err(err) + g.Err(err) err = el.Context(ctx).Select([]string{"a"}, true, rod.SelectorTypeText) - t.Err(err) + g.Err(err) err = el.Context(ctx).WaitStable(0) - t.Err(err) + g.Err(err) _, err = el.Context(ctx).Resource() - t.Err(err) + g.Err(err) err = el.Context(ctx).Input("a") - t.Err(err) + g.Err(err) err = el.Context(ctx).Input("a") - t.Err(err) + g.Err(err) _, err = el.Context(ctx).HTML() - t.Err(err) + g.Err(err) _, err = el.Context(ctx).Visible() - t.Err(err) + g.Err(err) _, err = el.Context(ctx).CanvasToImage("", 0) - t.Err(err) + g.Err(err) err = el.Context(ctx).Release() - t.Err(err) + g.Err(err) } diff --git a/go.mod b/go.mod index 7f113b5b..93aa01b7 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.16 require ( github.com/ysmood/goob v0.3.1 - github.com/ysmood/got v0.16.2 - github.com/ysmood/gotrace v0.2.2 - github.com/ysmood/gson v0.6.4 + github.com/ysmood/got v0.19.0 + github.com/ysmood/gotrace v0.4.0 + github.com/ysmood/gson v0.7.0 github.com/ysmood/leakless v0.7.0 ) diff --git a/go.sum b/go.sum index 2b96e3d2..01704552 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ github.com/ysmood/goob v0.3.1 h1:qMp5364BGS1DLJVrAqUxTF6KOFt0YDot8GC70u/0jbI= github.com/ysmood/goob v0.3.1/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs= -github.com/ysmood/got v0.16.2 h1:uedkfJYynagDxD2YfW6b7AySAVeh2IwchTwmqUGSREs= -github.com/ysmood/got v0.16.2/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= -github.com/ysmood/gotrace v0.2.2 h1:006KHGRThSRf8lwh4EyhNmuuq/l+Ygs+JqojkhEG1/E= -github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= -github.com/ysmood/gson v0.6.4 h1:Yb6tosv6bk59HqjZu2/7o4BFherpYEMkDkXmlhgryZ4= -github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/got v0.19.0 h1:AdsvaOIzsfRS+wqVvjUvIbK7MhuQI4qAmO2W6v/74Ko= +github.com/ysmood/got v0.19.0/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= +github.com/ysmood/gotrace v0.4.0 h1:NkiFGkr8AXxkEosLFudsvw5aGoBY5N7wFPOI4ZSCkb4= +github.com/ysmood/gotrace v0.4.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.7.0 h1:oQhY2FQtfy3+bgaNeqopd7NGAB6Me+UpG0n7oO4VDko= +github.com/ysmood/gson v0.7.0/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw= github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= diff --git a/hijack_test.go b/hijack_test.go index 6292ede1..ca1d8616 100644 --- a/hijack_test.go +++ b/hijack_test.go @@ -6,6 +6,7 @@ import ( "mime" "net/http" "sync" + "testing" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" @@ -13,8 +14,10 @@ import ( "github.com/ysmood/gson" ) -func (t T) Hijack() { - s := t.Serve() +func TestHijack(t *testing.T) { + g := setup(t) + + s := g.Serve() // to simulate a backend server s.Route("/", slash("fixtures/fetch.html")) @@ -23,21 +26,21 @@ func (t T) Hijack() { panic("wrong http method") } - t.Eq("header", r.Header.Get("Test")) + g.Eq("header", r.Header.Get("Test")) b, err := ioutil.ReadAll(r.Body) - t.E(err) - t.Eq("a", string(b)) + g.E(err) + g.Eq("a", string(b)) - t.HandleHTTP(".html", "test")(w, r) + g.HandleHTTP(".html", "test")(w, r) }) s.Route("/b", "", "b") - router := t.page.HijackRequests() + router := g.page.HijackRequests() defer router.MustStop() router.MustAdd(s.URL("/a"), func(ctx *rod.Hijack) { - r := ctx.Request.SetContext(t.Context()) + r := ctx.Request.SetContext(g.Context()) r.Req().Header.Set("Test", "header") // override request header r.SetBody([]byte("test")) // override request body r.SetBody(123) // override request body @@ -49,25 +52,25 @@ func (t T) Hijack() { ctx.CustomState = &MyState{10} - t.Eq(http.MethodPost, r.Method()) - t.Eq(s.URL("/a"), r.URL().String()) + g.Eq(http.MethodPost, r.Method()) + g.Eq(s.URL("/a"), r.URL().String()) - t.Eq(proto.NetworkResourceTypeXHR, ctx.Request.Type()) - t.Is(ctx.Request.IsNavigation(), false) - t.Has(ctx.Request.Header("Origin"), s.URL()) - t.Len(ctx.Request.Headers(), 6) - t.True(ctx.Request.JSONBody().Nil()) + g.Eq(proto.NetworkResourceTypeXHR, ctx.Request.Type()) + g.Is(ctx.Request.IsNavigation(), false) + g.Has(ctx.Request.Header("Origin"), s.URL()) + g.Len(ctx.Request.Headers(), 6) + g.True(ctx.Request.JSONBody().Nil()) // send request load response from real destination as the default value to hijack ctx.MustLoadResponse() - t.Eq(200, ctx.Response.Payload().ResponseCode) + g.Eq(200, ctx.Response.Payload().ResponseCode) // override status code ctx.Response.Payload().ResponseCode = http.StatusCreated - t.Eq("4", ctx.Response.Headers().Get("Content-Length")) - t.Has(ctx.Response.Headers().Get("Content-Type"), "text/html; charset=utf-8") + g.Eq("4", ctx.Response.Headers().Get("Content-Length")) + g.Has(ctx.Response.Headers().Get("Content-Type"), "text/html; charset=utf-8") // override response header ctx.Response.SetHeader("Set-Cookie", "key=val") @@ -79,7 +82,7 @@ func (t T) Hijack() { "text": "test", }) - t.Eq("{\"text\":\"test\"}", ctx.Response.Body()) + g.Eq("{\"text\":\"test\"}", ctx.Response.Body()) }) router.MustAdd(s.URL("/b"), func(ctx *rod.Hijack) { @@ -94,16 +97,18 @@ func (t T) Hijack() { go router.Run() - t.page.MustNavigate(s.URL()) + g.page.MustNavigate(s.URL()) - t.Eq("201 test key=val", t.page.MustElement("#a").MustText()) - t.Eq("b", t.page.MustElement("#b").MustText()) + g.Eq("201 test key=val", g.page.MustElement("#a").MustText()) + g.Eq("b", g.page.MustElement("#b").MustText()) } -func (t T) HijackContinue() { - s := t.Serve().Route("/", ".html", `ok`) +func TestHijackContinue(t *testing.T) { + g := setup(t) + + s := g.Serve().Route("/", ".html", `ok`) - router := t.page.HijackRequests() + router := g.page.HijackRequests() defer router.MustStop() wg := &sync.WaitGroup{} @@ -115,14 +120,16 @@ func (t T) HijackContinue() { go router.Run() - t.page.MustNavigate(s.URL("/a")) + g.page.MustNavigate(s.URL("/a")) - t.Eq("ok", t.page.MustElement("body").MustText()) + g.Eq("ok", g.page.MustElement("body").MustText()) wg.Wait() } -func (t T) HijackMockWholeResponse() { - router := t.page.HijackRequests() +func TestHijackMockWholeResponse(t *testing.T) { + g := setup(t) + + router := g.page.HijackRequests() defer router.MustStop() router.MustAdd("*", func(ctx *rod.Hijack) { @@ -132,15 +139,17 @@ func (t T) HijackMockWholeResponse() { go router.Run() - t.page.MustNavigate("http://test.com") + g.page.MustNavigate("http://test.com") - t.Eq("ok", t.page.MustElement("body").MustText()) + g.Eq("ok", g.page.MustElement("body").MustText()) } -func (t T) HijackSkip() { - s := t.Serve() +func TestHijackSkip(t *testing.T) { + g := setup(t) - router := t.page.HijackRequests() + s := g.Serve() + + router := g.page.HijackRequests() defer router.MustStop() wg := &sync.WaitGroup{} @@ -156,15 +165,17 @@ func (t T) HijackSkip() { go router.Run() - t.page.MustNavigate(s.URL("/a")) + g.page.MustNavigate(s.URL("/a")) wg.Wait() } -func (t T) HijackOnErrorLog() { - s := t.Serve().Route("/", ".html", `ok`) +func TestHijackOnErrorLog(t *testing.T) { + g := setup(t) + + s := g.Serve().Route("/", ".html", `ok`) - router := t.page.HijackRequests() + router := g.page.HijackRequests() defer router.MustStop() wg := &sync.WaitGroup{} @@ -181,20 +192,22 @@ func (t T) HijackOnErrorLog() { go router.Run() - t.mc.stub(1, proto.FetchContinueRequest{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.FetchContinueRequest{}, func(send StubSend) (gson.JSON, error) { return gson.New(nil), errors.New("err") }) go func() { - _ = t.page.Context(t.Context()).Navigate(s.URL("/a")) + _ = g.page.Context(g.Context()).Navigate(s.URL("/a")) }() wg.Wait() - t.Eq(err.Error(), "err") + g.Eq(err.Error(), "err") } -func (t T) HijackFailRequest() { - s := t.Serve().Route("/page", ".html", ` +func TestHijackFailRequest(t *testing.T) { + g := setup(t) + + s := g.Serve().Route("/page", ".html", ` `) - router := t.browser.HijackRequests() + router := g.browser.HijackRequests() defer router.MustStop() router.MustAdd(s.URL("/a"), func(ctx *rod.Hijack) { @@ -211,21 +224,23 @@ func (t T) HijackFailRequest() { go router.Run() - t.page.MustNavigate(s.URL("/page")).MustWaitLoad() + g.page.MustNavigate(s.URL("/page")).MustWaitLoad() - t.page.MustWait(`() => document.title === 'Failed to fetch'`) + g.page.MustWait(`() => document.title === 'Failed to fetch'`) { // test error log - t.mc.stub(1, proto.FetchFailRequest{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.FetchFailRequest{}, func(send StubSend) (gson.JSON, error) { _, _ = send() return gson.JSON{}, errors.New("err") }) - _ = t.page.Navigate(s.URL("/a")) + _ = g.page.Navigate(s.URL("/a")) } } -func (t T) HijackLoadResponseErr() { - p := t.newPage().Context(t.Context()) +func TestHijackLoadResponseErr(t *testing.T) { + g := setup(t) + + p := g.newPage().Context(g.Context()) router := p.HijackRequests() defer router.MustStop() @@ -233,11 +248,11 @@ func (t T) HijackLoadResponseErr() { wg.Add(1) router.MustAdd("http://test.com/a", func(ctx *rod.Hijack) { - t.Err(ctx.LoadResponse(&http.Client{ + g.Err(ctx.LoadResponse(&http.Client{ Transport: &MockRoundTripper{err: errors.New("err")}, }, true)) - t.Err(ctx.LoadResponse(&http.Client{ + g.Err(ctx.LoadResponse(&http.Client{ Transport: &MockRoundTripper{res: &http.Response{ StatusCode: 200, Body: ioutil.NopCloser(&MockReader{err: errors.New("err")}), @@ -256,10 +271,12 @@ func (t T) HijackLoadResponseErr() { wg.Wait() } -func (t T) HijackResponseErr() { - s := t.Serve().Route("/", ".html", `ok`) +func TestHijackResponseErr(t *testing.T) { + g := setup(t) - p := t.newPage().Context(t.Context()) + s := g.Serve().Route("/", ".html", `ok`) + + p := g.newPage().Context(g.Context()) router := p.HijackRequests() defer router.MustStop() @@ -268,12 +285,12 @@ func (t T) HijackResponseErr() { router.MustAdd(s.URL("/a"), func(ctx *rod.Hijack) { // to ignore favicon ctx.OnError = func(err error) { - t.Err(err) + g.Err(err) wg.Done() } ctx.MustLoadResponse() - t.mc.stub(1, proto.FetchFulfillRequest{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.FetchFulfillRequest{}, func(send StubSend) (gson.JSON, error) { res, _ := send() return res, errors.New("err") }) @@ -286,8 +303,10 @@ func (t T) HijackResponseErr() { wg.Wait() } -func (t T) HandleAuth() { - s := t.Serve() +func TestHandleAuth(t *testing.T) { + g := setup(t) + + s := g.Serve() // mock the server s.Mux.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) { @@ -298,24 +317,24 @@ func (t T) HandleAuth() { return } - t.Eq("a", u) - t.Eq("b", p) - t.HandleHTTP(".html", `

ok

`)(w, r) + g.Eq("a", u) + g.Eq("b", p) + g.HandleHTTP(".html", `

ok

`)(w, r) }) s.Route("/err", ".html", "err page") - go t.browser.MustHandleAuth("a", "b")() + go g.browser.MustHandleAuth("a", "b")() - page := t.newPage(s.URL("/a")) + page := g.newPage(s.URL("/a")) page.MustElementR("p", "ok") - wait := t.browser.HandleAuth("a", "b") + wait := g.browser.HandleAuth("a", "b") var page2 *rod.Page wait2 := utils.All(func() { - page2, _ = t.browser.Page(proto.TargetCreateTarget{URL: s.URL("/err")}) + page2, _ = g.browser.Page(proto.TargetCreateTarget{URL: s.URL("/err")}) }) - t.mc.stubErr(1, proto.FetchContinueRequest{}) - t.Err(wait()) + g.mc.stubErr(1, proto.FetchContinueRequest{}) + g.Err(wait()) wait2() page2.MustClose() } diff --git a/lib/cdp/client_private_test.go b/lib/cdp/client_private_test.go index 53c9fba8..405009a3 100644 --- a/lib/cdp/client_private_test.go +++ b/lib/cdp/client_private_test.go @@ -11,13 +11,7 @@ import ( "github.com/ysmood/got" ) -func Test(t *testing.T) { - got.Each(t, T{}) -} - -type T struct { - got.G -} +var setup = got.Setup(nil) type MockWebSocket struct { send func([]byte) error @@ -37,18 +31,22 @@ func (c *MockWebSocket) Read() ([]byte, error) { return c.read() } -func (t T) CancelCall() { +func TestCancelCall(t *testing.T) { + g := setup(t) + cdp := New("") go func() { <-cdp.chReq }() - cdp.ctx = t.Context() - _, err := cdp.Call(t.Timeout(0), "", "", nil) - t.Err(err) + cdp.ctx = g.Context() + _, err := cdp.Call(g.Timeout(0), "", "", nil) + g.Err(err) } -func (t T) ReqErr() { - ctx := t.Context() +func TestReqErr(t *testing.T) { + g := setup(t) + + ctx := g.Context() cdp := New("") cdp.ctx = ctx cdp.close = ctx.Cancel @@ -56,19 +54,23 @@ func (t T) ReqErr() { send: func([]byte) error { return errors.New("err") }, } - _, err := cdp.Call(t.Context(), "", "", nil) - t.Err(err) + _, err := cdp.Call(g.Context(), "", "", nil) + g.Err(err) } -func (t T) CancelBeforeSend() { +func TestCancelBeforeSend(t *testing.T) { + g := setup(t) + cdp := New("") - cdp.ctx = t.Context() - _, err := cdp.Call(t.Timeout(0), "", "", nil) - t.Eq(err, context.DeadlineExceeded) + cdp.ctx = g.Context() + _, err := cdp.Call(g.Timeout(0), "", "", nil) + g.Eq(err, context.DeadlineExceeded) } -func (t T) CancelBeforeCallback() { - ctx := t.Context() +func TestCancelBeforeCallback(t *testing.T) { + g := setup(t) + + ctx := g.Context() cdp := New("") cdp.ws = &MockWebSocket{ read: func() ([]byte, error) { @@ -84,11 +86,13 @@ func (t T) CancelBeforeCallback() { cdp.MustConnect(ctx) _, err := cdp.Call(ctx, "", "", nil) - t.Eq(err.Error(), "context canceled") + g.Eq(err.Error(), "context canceled") } -func (t T) CancelOnReadRes() { - ctx := t.Context() +func TestCancelOnReadRes(t *testing.T) { + g := setup(t) + + ctx := g.Context() cdp := New("") cdp.ws = &MockWebSocket{ send: func(bytes []byte) error { @@ -106,11 +110,13 @@ func (t T) CancelOnReadRes() { cdp.MustConnect(ctx) _, err := cdp.Call(ctx, "", "", nil) - t.Err(err) + g.Err(err) } -func (t T) CallAfterBrowserDone() { - ctx := t.Context() +func TestCallAfterBrowserDone(t *testing.T) { + g := setup(t) + + ctx := g.Context() cdp := New("") cdp.ws = &MockWebSocket{ send: func(bytes []byte) error { return io.EOF }, @@ -120,13 +126,15 @@ func (t T) CallAfterBrowserDone() { utils.Sleep(0.1) _, err := cdp.Call(ctx, "", "", nil) - t.Err(err) - t.Is(err, io.EOF) - t.Eq(err.Error(), "cdp connection closed: EOF") + g.Err(err) + g.Is(err, io.EOF) + g.Eq(err.Error(), "cdp connection closed: EOF") } -func (t T) CancelOnReadEvent() { - ctx, cancel := context.WithCancel(t.Context()) +func TestCancelOnReadEvent(t *testing.T) { + g := setup(t) + + ctx, cancel := context.WithCancel(g.Context()) cdp := New("") cdp.ws = &MockWebSocket{ send: func(bytes []byte) error { @@ -139,21 +147,25 @@ func (t T) CancelOnReadEvent() { } cdp.MustConnect(ctx) - _, err := cdp.Call(t.Context(), "", "", nil) - t.Err(err) + _, err := cdp.Call(g.Context(), "", "", nil) + g.Err(err) } -func (t T) TestError() { - t.Is(&Error{Code: -123}, &Error{Code: -123}) +func TestTestError(t *testing.T) { + g := setup(t) + + g.Is(&Error{Code: -123}, &Error{Code: -123}) } -func (t T) PendingRequests() { +func TestPendingRequests(t *testing.T) { + g := setup(t) + pending := newPendingRequests() err := pending.add(1, newPendingRequest()) - t.Nil(err) + g.Nil(err) err = pending.add(2, newPendingRequest()) - t.Nil(err) + g.Nil(err) pending.fulfill(1, &Response{}) // resolving something where no-one is waiting is fine @@ -165,12 +177,12 @@ func (t T) PendingRequests() { pending.close(errors.New("this will be ignored")) err = pending.add(3, newPendingRequest()) - t.Is(err, io.EOF) - t.Err(err) + g.Is(err, io.EOF) + g.Err(err) pending = newPendingRequests() pending.close(nil) err = pending.add(3, newPendingRequest()) - t.Err(err) - t.Eq(err.Error(), "browser has shut down") + g.Err(err) + g.Eq(err.Error(), "browser has shut down") } diff --git a/lib/cdp/client_test.go b/lib/cdp/client_test.go index 291f724b..0ab1f44f 100644 --- a/lib/cdp/client_test.go +++ b/lib/cdp/client_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "net/http" + "os" "path/filepath" "testing" "time" @@ -20,20 +21,20 @@ import ( var loud = flag.Bool("loud", false, "log everything") -func Test(t *testing.T) { +func TestMain(m *testing.M) { if !*loud { log.SetOutput(ioutil.Discard) } - got.Each(t, T{}) + os.Exit(m.Run()) } -type T struct { - got.G -} +var setup = got.Setup(nil) + +func TestBasic(t *testing.T) { + g := setup(t) -func (t T) Basic() { - ctx := t.Context() + ctx := g.Context() url := launcher.New().MustLaunch() @@ -51,12 +52,12 @@ func (t T) Basic() { }() file, err := filepath.Abs(filepath.FromSlash("fixtures/iframe.html")) - t.E(err) + g.E(err) res, err := client.Call(ctx, "", "Target.createTarget", map[string]string{ "url": "file://" + file, }) - t.E(err) + g.E(err) targetID := gson.New(res).Get("targetId").String() @@ -64,19 +65,19 @@ func (t T) Basic() { "targetId": targetID, "flatten": true, // if it's not set no response will return }) - t.E(err) + g.E(err) sessionID := gson.New(res).Get("sessionId").String() _, err = client.Call(ctx, sessionID, "Page.enable", nil) - t.E(err) + g.E(err) _, err = client.Call(ctx, "", "Target.attachToTarget", map[string]interface{}{ "targetId": "abc", }) - t.Err(err) + g.Err(err) - timeout := t.Context() + timeout := g.Context() sleeper := func() utils.Sleeper { return utils.BackoffSleeper(30*time.Millisecond, 3*time.Second, nil) @@ -88,9 +89,9 @@ func (t T) Basic() { _, err = client.Call(tmpCtx, sessionID, "Runtime.evaluate", map[string]interface{}{ "expression": `10`, }) - t.Eq(err.Error(), context.Canceled.Error()) + g.Eq(err.Error(), context.Canceled.Error()) - t.E(utils.Retry(timeout, sleeper(), func() (bool, error) { + g.E(utils.Retry(timeout, sleeper(), func() (bool, error) { res, err = client.Call(ctx, sessionID, "Runtime.evaluate", map[string]interface{}{ "expression": `document.querySelector('iframe')`, }) @@ -101,19 +102,19 @@ func (t T) Basic() { res, err = client.Call(ctx, sessionID, "DOM.describeNode", map[string]interface{}{ "objectId": gson.New(res).Get("result.objectId").String(), }) - t.E(err) + g.E(err) frameID := gson.New(res).Get("node.frameId").String() - timeout = t.Context() + timeout = g.Context() - t.E(utils.Retry(timeout, sleeper(), func() (bool, error) { + g.E(utils.Retry(timeout, sleeper(), func() (bool, error) { // we might need to recreate the world because world can be // destroyed after the frame is reloaded res, err = client.Call(ctx, sessionID, "Page.createIsolatedWorld", map[string]interface{}{ "frameId": frameID, }) - t.E(err) + g.E(err) res, err = client.Call(ctx, sessionID, "Runtime.evaluate", map[string]interface{}{ "contextId": gson.New(res).Get("executionContextId").Int(), @@ -126,29 +127,34 @@ func (t T) Basic() { res, err = client.Call(ctx, sessionID, "DOM.getOuterHTML", map[string]interface{}{ "objectId": gson.New(res).Get("result.objectId").String(), }) - t.E(err) + g.E(err) - t.Eq("

it works

", gson.New(res).Get("outerHTML").String()) + g.Eq("

it works

", gson.New(res).Get("outerHTML").String()) } -func (t T) TestError() { +func TestTestError(t *testing.T) { + g := setup(t) + cdpErr := cdp.Error{10, "err", "data"} - t.Eq(cdpErr.Error(), "{10 err data}") + g.Eq(cdpErr.Error(), "{10 err data}") - t.Panic(func() { - cdp.New("").MustConnect(t.Context()) + g.Panic(func() { + cdp.New("").MustConnect(g.Context()) }) } -func (t T) NewWithLogger() { +func TestNewWithLogger(t *testing.T) { + g := setup(t) - t.Panic(func() { - cdp.New("").MustConnect(t.Context()) + g.Panic(func() { + cdp.New("").MustConnect(g.Context()) }) } -func (t T) Crash() { - ctx := t.Context() +func TestCrash(t *testing.T) { + g := setup(t) + + ctx := g.Context() l := launcher.New() client := cdp.New(l.MustLaunch()).Logger(utils.LoggerQuiet).MustConnect(ctx) @@ -159,12 +165,12 @@ func (t T) Crash() { }() file, err := filepath.Abs(filepath.FromSlash("fixtures/iframe.html")) - t.E(err) + g.E(err) res, err := client.Call(ctx, "", "Target.createTarget", map[string]interface{}{ "url": "file://" + file, }) - t.E(err) + g.E(err) targetID := gson.New(res).Get("targetId").String() @@ -172,12 +178,12 @@ func (t T) Crash() { "targetId": targetID, "flatten": true, }) - t.E(err) + g.E(err) sessionID := gson.New(res).Get("sessionId").String() _, err = client.Call(ctx, sessionID, "Page.enable", nil) - t.E(err) + g.E(err) go func() { utils.Sleep(2) @@ -188,6 +194,6 @@ func (t T) Crash() { "expression": `new Promise(() => {})`, "awaitPromise": true, }) - t.Is(err, cdp.ErrConnClosed) - t.Eq(err.Error(), "cdp connection closed: EOF") + g.Is(err, cdp.ErrConnClosed) + g.Eq(err.Error(), "cdp connection closed: EOF") } diff --git a/lib/cdp/websocket_private_test.go b/lib/cdp/websocket_private_test.go index a9aab3b4..3054f252 100644 --- a/lib/cdp/websocket_private_test.go +++ b/lib/cdp/websocket_private_test.go @@ -6,37 +6,40 @@ import ( "errors" "net" "net/url" + "testing" "time" ) -func (t T) WebSocketErr() { +func TestWebSocketErr(t *testing.T) { + g := setup(t) + ws := WebSocket{} - t.Err(ws.Connect(t.Context(), "://", nil)) + g.Err(ws.Connect(g.Context(), "://", nil)) ws.Dialer = &net.Dialer{} ws.initDialer(nil) u, err := url.Parse("wss://no-exist") - t.E(err) + g.E(err) ws.Dialer = nil ws.initDialer(u) mc := &MockConn{} ws.conn = mc - t.Err(ws.Send([]byte("test"))) + g.Err(ws.Send([]byte("test"))) mc.errOnCount = 1 mc.frame = []byte{0, 127, 1} ws.r = bufio.NewReader(mc) - t.Err(ws.Read()) + g.Err(ws.Read()) - t.Err(ws.handshake(t.Timeout(0), nil, nil)) + g.Err(ws.handshake(g.Timeout(0), nil, nil)) mc.errOnCount = 1 - t.Err(ws.handshake(t.Context(), u, nil)) + g.Err(ws.handshake(g.Context(), u, nil)) tls := &tlsDialer{} - t.Err(tls.DialContext(context.Background(), "", "")) + g.Err(tls.DialContext(context.Background(), "", "")) } type MockConn struct { diff --git a/lib/cdp/websocket_test.go b/lib/cdp/websocket_test.go index 8564b6bb..2deca453 100644 --- a/lib/cdp/websocket_test.go +++ b/lib/cdp/websocket_test.go @@ -6,48 +6,54 @@ import ( "net/http" "path/filepath" "strings" + "testing" "github.com/go-rod/rod/lib/cdp" "github.com/go-rod/rod/lib/launcher" + "github.com/ysmood/got" "github.com/ysmood/gson" ) -func (t T) WebSocketLargePayload() { - ctx := t.Context() - client, id := t.newPage(ctx) +func TestWebSocketLargePayload(t *testing.T) { + g := setup(t) + + ctx := g.Context() + client, id := newPage(ctx, g) res, err := client.Call(ctx, id, "Runtime.evaluate", map[string]interface{}{ "expression": fmt.Sprintf(`"%s"`, strings.Repeat("a", 2*1024*1024)), "returnByValue": true, }) - t.E(err) - t.Gt(res, 2*1024*1024) // 2MB + g.E(err) + g.Gt(res, 2*1024*1024) // 2MB } -func (t T) WebSocketHeader() { - s := t.Serve() +func TestWebSocketHeader(t *testing.T) { + g := setup(t) + + s := g.Serve() wait := make(chan struct{}) s.Mux.HandleFunc("/a", func(rw http.ResponseWriter, r *http.Request) { - t.Eq(r.Header.Get("Test"), "header") - t.Eq(r.Host, "test.com") - t.Eq(r.URL.Query().Get("q"), "ok") + g.Eq(r.Header.Get("Test"), "header") + g.Eq(r.Host, "test.com") + g.Eq(r.URL.Query().Get("q"), "ok") close(wait) }) ws := cdp.WebSocket{} - err := ws.Connect(t.Context(), s.URL("/a?q=ok"), http.Header{ + err := ws.Connect(g.Context(), s.URL("/a?q=ok"), http.Header{ "Host": {"test.com"}, "Test": {"header"}, }) <-wait - t.Eq(err.Error(), "websocket bad handshake: 200 OK. ") + g.Eq(err.Error(), "websocket bad handshake: 200 OK. ") } -func (t T) newPage(ctx context.Context) (*cdp.Client, string) { +func newPage(ctx context.Context, g got.G) (*cdp.Client, string) { l := launcher.New() - t.Cleanup(l.Kill) + g.Cleanup(l.Kill) client := cdp.New(l.MustLaunch()).MustConnect(ctx) @@ -57,12 +63,12 @@ func (t T) newPage(ctx context.Context) (*cdp.Client, string) { }() file, err := filepath.Abs(filepath.FromSlash("fixtures/basic.html")) - t.E(err) + g.E(err) res, err := client.Call(ctx, "", "Target.createTarget", map[string]interface{}{ "url": "file://" + file, }) - t.E(err) + g.E(err) targetID := gson.New(res).Get("targetId").String() @@ -70,23 +76,25 @@ func (t T) newPage(ctx context.Context) (*cdp.Client, string) { "targetId": targetID, "flatten": true, }) - t.E(err) + g.E(err) sessionID := gson.New(res).Get("sessionId").String() return client, sessionID } -func (t T) DuplicatedConnectErr() { +func TestDuplicatedConnectErr(t *testing.T) { + g := setup(t) + l := launcher.New() - t.Cleanup(l.Kill) + g.Cleanup(l.Kill) u := l.MustLaunch() ws := &cdp.WebSocket{} - t.E(ws.Connect(t.Context(), u, nil)) + g.E(ws.Connect(g.Context(), u, nil)) - t.Panic(func() { - _ = ws.Connect(t.Context(), u, nil) + g.Panic(func() { + _ = ws.Connect(g.Context(), u, nil) }) } diff --git a/lib/js/helper.js b/lib/js/helper.js index df1e8fd3..50fc3051 100644 --- a/lib/js/helper.js +++ b/lib/js/helper.js @@ -2,7 +2,7 @@ // To debug just add "debugger" keyword to the line you want to pause, then run something like: // // go run ./lib/js/generate -// rod=show,devtools go test -run /Click +// rod=show,devtools go test -run ^TestClick$ const functions = { element(selector) { diff --git a/lib/launcher/launcher_test.go b/lib/launcher/launcher_test.go index 9b6406ca..b4894847 100644 --- a/lib/launcher/launcher_test.go +++ b/lib/launcher/launcher_test.go @@ -21,23 +21,19 @@ import ( "github.com/ysmood/got" ) -type T struct { - got.G -} +var setup = got.Setup(nil) -func Test(t *testing.T) { - launcher.NewBrowser().MustGet() // preload browser to local +func TestDownloadHosts(t *testing.T) { + g := setup(t) - got.Each(t, T{}) + g.Has(launcher.HostGoogle(launcher.DefaultRevision), "https://storage.googleapis.com/chromium-browser-snapshots") + g.Has(launcher.HostNPM(launcher.DefaultRevision), "https://registry.npmmirror.com/-/binary/chromium-browser-snapshots") } -func (t T) DownloadHosts() { - t.Has(launcher.HostGoogle(launcher.DefaultRevision), "https://storage.googleapis.com/chromium-browser-snapshots") - t.Has(launcher.HostNPM(launcher.DefaultRevision), "https://registry.npmmirror.com/-/binary/chromium-browser-snapshots") -} +func TestDownload(t *testing.T) { + g := setup(t) -func (t T) Download() { - s := t.Serve() + s := g.Serve() s.Mux.HandleFunc("/fast/", func(rw http.ResponseWriter, r *http.Request) { buf := bytes.NewBuffer(nil) zw := zip.NewWriter(buf) @@ -46,15 +42,15 @@ func (t T) Download() { h := &zip.FileHeader{Name: "to/"} h.SetMode(0755) _, err := zw.CreateHeader(h) - t.E(err) + g.E(err) // file "file.txt" w, err := zw.CreateHeader(&zip.FileHeader{Name: "to/file.txt"}) - t.E(err) - b := []byte(t.Srand(2 * 1024 * 1024)) - t.E(w.Write(b)) + g.E(err) + b := []byte(g.RandStr(2 * 1024 * 1024)) + g.E(w.Write(b)) - t.E(zw.Close()) + g.E(zw.Close()) rw.Header().Add("Content-Length", fmt.Sprintf("%d", buf.Len())) _, _ = io.Copy(rw, buf) @@ -72,16 +68,20 @@ func (t T) Download() { b.Logger = ioutil.Discard defer cancel() b.Hosts = []launcher.Host{launcher.HostTest(s.URL("/slow")), launcher.HostTest(s.URL("/fast"))} - b.Dir = filepath.Join("tmp", "browser-from-mirror", t.Srand(16)) - t.E(b.Download()) - t.Nil(os.Stat(b.Dir)) + b.Dir = filepath.Join("tmp", "browser-from-mirror", g.RandStr(16)) + g.E(b.Download()) + g.Nil(os.Stat(b.Dir)) } -func (t T) BrowserGet() { - t.Nil(os.Stat(launcher.NewBrowser().MustGet())) +func TestBrowserGet(t *testing.T) { + g := setup(t) + + g.Nil(os.Stat(launcher.NewBrowser().MustGet())) } -func (t T) Launch() { +func TestLaunch(t *testing.T) { + g := setup(t) + defaults.Proxy = "test.com" defer func() { defaults.ResetWithEnv("") }() @@ -89,49 +89,51 @@ func (t T) Launch() { defer l.Kill() u := l.MustLaunch() - t.Regex(`\Aws://.+\z`, u) + g.Regex(`\Aws://.+\z`, u) parsed, _ := url.Parse(u) { // test GetWebSocketDebuggerURL for _, prefix := range []string{"", ":", "127.0.0.1:", "ws://127.0.0.1:"} { u2 := launcher.MustResolveURL(prefix + parsed.Port()) - t.Regex(u, u2) + g.Regex(u, u2) } _, err := launcher.ResolveURL("") - t.Err(err) + g.Err(err) } { _, err := launcher.NewManaged("") - t.Err(err) + g.Err(err) _, err = launcher.NewManaged("1://") - t.Err(err) + g.Err(err) _, err = launcher.NewManaged("ws://not-exists") - t.Err(err) + g.Err(err) } } -func (t T) LaunchUserMode() { +func TestLaunchUserMode(t *testing.T) { + g := setup(t) + l := launcher.NewUserMode() defer l.Kill() l.Kill() // empty kill should do nothing has := l.Has("not-exists") - t.False(has) + g.False(has) l.Append("test-append", "a") f := l.Get("test-append") - t.Eq("a", f) + g.Eq("a", f) dir := l.Get(flags.UserDataDir) port := 58472 - url := l.Context(t.Context()).Delete("test").Bin(""). + url := l.Context(g.Context()).Delete("test").Bin(""). Revision(launcher.DefaultRevision). Logger(ioutil.Discard). Leakless(false).Leakless(true). @@ -145,30 +147,36 @@ func (t T) LaunchUserMode() { Env("TZ=Asia/Tokyo"). MustLaunch() - t.Eq(url, launcher.NewUserMode().RemoteDebuggingPort(port).MustLaunch()) + g.Eq(url, launcher.NewUserMode().RemoteDebuggingPort(port).MustLaunch()) } -func (t T) UserModeErr() { +func TestUserModeErr(t *testing.T) { + g := setup(t) + _, err := launcher.NewUserMode().RemoteDebuggingPort(48277).Bin("not-exists").Launch() - t.Err(err) + g.Err(err) _, err = launcher.NewUserMode().RemoteDebuggingPort(58217).Bin("echo").Launch() - t.Err(err) + g.Err(err) } -func (t T) GetWebSocketDebuggerURLErr() { +func TestGetWebSocketDebuggerURLErr(t *testing.T) { + g := setup(t) + _, err := launcher.ResolveURL("1://") - t.Err(err) + g.Err(err) } -func (t T) LaunchErr() { - t.Panic(func() { +func TestLaunchErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { launcher.New().Bin("not-exists").MustLaunch() }) - t.Panic(func() { + g.Panic(func() { launcher.New().Headless(false).Bin("not-exists").MustLaunch() }) - t.Panic(func() { + g.Panic(func() { launcher.New().Client() }) { @@ -190,12 +198,14 @@ func newBrowser() (*launcher.Browser, func()) { var testProfileDir = flag.Bool("test-profile-dir", false, "set it to test profile dir") -func (t T) ProfileDir() { +func TestProfileDir(t *testing.T) { + g := setup(t) + url := launcher.New().Headless(false). ProfileDir("").ProfileDir("test-profile-dir") if !*testProfileDir { - t.Skip("It's not CI friendly, so we skip it!") + g.Skip("It's not CI friendly, so we skip it!") } url.MustLaunch() @@ -203,6 +213,6 @@ func (t T) ProfileDir() { userDataDir := url.Get(flags.UserDataDir) file, err := os.Stat(filepath.Join(userDataDir, "test-profile-dir")) - t.E(err) - t.True(file.IsDir()) + g.E(err) + g.True(file.IsDir()) } diff --git a/lib/launcher/private_test.go b/lib/launcher/private_test.go index 1c65b2b5..22986ba0 100644 --- a/lib/launcher/private_test.go +++ b/lib/launcher/private_test.go @@ -30,37 +30,43 @@ func HostTest(host string) Host { } } -type T struct { - got.G -} +var setup = got.Setup(nil) -func TestPrivate(t *testing.T) { +func TestMain(m *testing.M) { NewBrowser().MustGet() // preload browser to local - got.Each(t, T{}) + os.Exit(m.Run()) } -func (t T) ToHTTP() { +func TestToHTTP(t *testing.T) { + g := setup(t) + u, _ := url.Parse("wss://a.com") - t.Eq("https", toHTTP(*u).Scheme) + g.Eq("https", toHTTP(*u).Scheme) u, _ = url.Parse("ws://a.com") - t.Eq("http", toHTTP(*u).Scheme) + g.Eq("http", toHTTP(*u).Scheme) } -func (t T) ToWS() { +func TestToWS(t *testing.T) { + g := setup(t) + u, _ := url.Parse("https://a.com") - t.Eq("wss", toWS(*u).Scheme) + g.Eq("wss", toWS(*u).Scheme) u, _ = url.Parse("http://a.com") - t.Eq("ws", toWS(*u).Scheme) + g.Eq("ws", toWS(*u).Scheme) } -func (t T) Unzip() { - t.Err(unzip(ioutil.Discard, "", "")) +func TestUnzip(t *testing.T) { + g := setup(t) + + g.Err(unzip(ioutil.Discard, "", "")) } -func (t T) LaunchOptions() { +func TestLaunchOptions(t *testing.T) { + g := setup(t) + defaults.Show = true defaults.Devtools = true inContainer = true @@ -73,19 +79,21 @@ func (t T) LaunchOptions() { l := New() - t.False(l.Has(flags.Headless)) + g.False(l.Has(flags.Headless)) - t.True(l.Has(flags.NoSandbox)) + g.True(l.Has(flags.NoSandbox)) - t.True(l.Has("auto-open-devtools-for-tabs")) + g.True(l.Has("auto-open-devtools-for-tabs")) } -func (t T) GetURLErr() { +func TestGetURLErr(t *testing.T) { + g := setup(t) + l := New() l.ctxCancel() _, err := l.getURL() - t.Err(err) + g.Err(err) l = New() l.parser.lock.Lock() @@ -93,20 +101,22 @@ func (t T) GetURLErr() { l.parser.lock.Unlock() close(l.exit) _, err = l.getURL() - t.Eq("[launcher] Failed to get the debug url: err", err.Error()) + g.Eq("[launcher] Failed to get the debug url: err", err.Error()) } -func (t T) RemoteLaunch() { - ctx := t.Timeout(5 * time.Second) +func TestRemoteLaunch(t *testing.T) { + g := setup(t) - s := got.New(t).Serve() + ctx := g.Timeout(5 * time.Second) + + s := got.New(g).Serve() rl := NewManager() s.Mux.Handle("/", rl) l := MustNewManaged(s.URL()).KeepUserDataDir().Delete(flags.KeepUserDataDir) client := l.Client() b := client.MustConnect(ctx) - t.E(b.Call(ctx, "", "Browser.getVersion", nil)) + g.E(b.Call(ctx, "", "Browser.getVersion", nil)) utils.Sleep(1) _, _ = b.Call(ctx, "", "Browser.crash", nil) dir := l.Get(flags.UserDataDir) @@ -118,55 +128,63 @@ func (t T) RemoteLaunch() { break } } - t.Err(os.Stat(dir)) + g.Err(os.Stat(dir)) err := MustNewManaged(s.URL()).Bin("go").Client().Connect(ctx).(*cdp.ErrBadHandshake) - t.Eq(err.Body, "not allowed rod-bin path: go") + g.Eq(err.Body, "not allowed rod-bin path: go") } -func (t T) LaunchErrs() { +func TestLaunchErrs(t *testing.T) { + g := setup(t) + l := New().Bin("echo") _, err := l.Launch() - t.Err(err) + g.Err(err) - s := t.Serve() + s := g.Serve() s.Route("/", "", nil) l = New().Bin("") l.browser.Logger = ioutil.Discard - l.browser.Dir = filepath.Join("tmp", "browser-from-mirror", t.Srand(16)) + l.browser.Dir = filepath.Join("tmp", "browser-from-mirror", g.RandStr(16)) l.browser.Hosts = []Host{HostTest(s.URL())} _, err = l.Launch() - t.Err(err) + g.Err(err) } -func (t T) Progresser() { +func TestProgresser(t *testing.T) { + g := setup(t) + p := progresser{size: 100, logger: ioutil.Discard} - t.E(p.Write(make([]byte, 100))) - t.E(p.Write(make([]byte, 100))) - t.E(p.Write(make([]byte, 100))) + g.E(p.Write(make([]byte, 100))) + g.E(p.Write(make([]byte, 100))) + g.E(p.Write(make([]byte, 100))) } -func (t T) URLParserErr() { +func TestURLParserErr(t *testing.T) { + g := setup(t) + u := &URLParser{ Buffer: "error", lock: &sync.Mutex{}, } - t.Eq(u.Err().Error(), "[launcher] Failed to get the debug url: error") + g.Eq(u.Err().Error(), "[launcher] Failed to get the debug url: error") u.Buffer = "/tmp/rod/chromium-818858/chrome-linux/chrome: error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory" - t.Eq(u.Err().Error(), "[launcher] Failed to launch the browser, the doc might help https://go-rod.github.io/#/compatibility?id=os: /tmp/rod/chromium-818858/chrome-linux/chrome: error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory") + g.Eq(u.Err().Error(), "[launcher] Failed to launch the browser, the doc might help https://go-rod.github.io/#/compatibility?id=os: /tmp/rod/chromium-818858/chrome-linux/chrome: error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory") } -func (t T) BrowserDownloadErr() { +func TestBrowserDownloadErr(t *testing.T) { + g := setup(t) + b := NewBrowser() b.Logger = ioutil.Discard malURL := "https://npm.taobao.org/mirrors/chromium-browser-snapshots//869685/" - t.Has(b.download(t.Context(), malURL).Error(), "failed to download the browser: 200") + g.Has(b.download(g.Context(), malURL).Error(), "failed to download the browser: 200") } -func (t T) TestOpen() { +func TestTestOpen(t *testing.T) { openExec = func(name string, arg ...string) *exec.Cmd { cmd := exec.Command("not-exists") cmd.Process = &os.Process{} diff --git a/lib/utils/sleeper_test.go b/lib/utils/sleeper_test.go index b7a574c1..812e5f26 100644 --- a/lib/utils/sleeper_test.go +++ b/lib/utils/sleeper_test.go @@ -3,20 +3,25 @@ package utils_test import ( "context" "io" + "testing" "time" "github.com/go-rod/rod/lib/utils" ) -func (t T) BackoffSleeperWakeNow() { - t.E(utils.BackoffSleeper(0, 0, nil)(t.Context())) +func TestBackoffSleeperWakeNow(t *testing.T) { + g := setup(t) + + g.E(utils.BackoffSleeper(0, 0, nil)(g.Context())) } -func (t T) Retry() { +func TestRetry(t *testing.T) { + g := setup(t) + count := 0 s1 := utils.BackoffSleeper(1, 5, nil) - err := utils.Retry(t.Context(), s1, func() (bool, error) { + err := utils.Retry(g.Context(), s1, func() (bool, error) { if count > 5 { return true, io.EOF } @@ -24,11 +29,13 @@ func (t T) Retry() { return false, nil }) - t.Eq(err.Error(), io.EOF.Error()) + g.Eq(err.Error(), io.EOF.Error()) } -func (t T) RetryCancel() { - ctx := t.Context() +func TestRetryCancel(t *testing.T) { + g := setup(t) + + ctx := g.Context() go ctx.Cancel() s := utils.BackoffSleeper(time.Second, time.Second, nil) @@ -36,24 +43,30 @@ func (t T) RetryCancel() { return false, nil }) - t.Eq(err.Error(), context.Canceled.Error()) + g.Eq(err.Error(), context.Canceled.Error()) } -func (t T) CountSleeperErr() { - ctx := t.Context() +func TestCountSleeperErr(t *testing.T) { + g := setup(t) + + ctx := g.Context() s := utils.CountSleeper(5) for i := 0; i < 5; i++ { _ = s(ctx) } - t.Err(s(ctx)) + g.Err(s(ctx)) } -func (t T) CountSleeperCancel() { +func TestCountSleeperCancel(t *testing.T) { + g := setup(t) + s := utils.CountSleeper(5) - t.Eq(s(t.Timeout(0)), context.DeadlineExceeded) + g.Eq(s(g.Timeout(0)), context.DeadlineExceeded) } -func (t T) EachSleepers() { +func TestEachSleepers(t *testing.T) { + g := setup(t) + s1 := utils.BackoffSleeper(1, 5, nil) s2 := utils.CountSleeper(5) s := utils.EachSleepers(s1, s2) @@ -62,11 +75,13 @@ func (t T) EachSleepers() { return false, nil }) - t.Is(err, &utils.ErrMaxSleepCount{}) - t.Eq(err.Error(), "max sleep count 5 exceeded") + g.Is(err, &utils.ErrMaxSleepCount{}) + g.Eq(err.Error(), "max sleep count 5 exceeded") } -func (t T) RaceSleepers() { +func TestRaceSleepers(t *testing.T) { + g := setup(t) + s1 := utils.BackoffSleeper(1, 5, nil) s2 := utils.CountSleeper(5) s := utils.RaceSleepers(s1, s2) @@ -75,6 +90,6 @@ func (t T) RaceSleepers() { return false, nil }) - t.Is(err, &utils.ErrMaxSleepCount{}) - t.Eq(err.Error(), "max sleep count 5 exceeded") + g.Is(err, &utils.ErrMaxSleepCount{}) + g.Eq(err.Error(), "max sleep count 5 exceeded") } diff --git a/lib/utils/utils_test.go b/lib/utils/utils_test.go index 03b82c06..db8e440d 100644 --- a/lib/utils/utils_test.go +++ b/lib/utils/utils_test.go @@ -15,35 +15,35 @@ import ( "github.com/ysmood/got" ) -type T struct { - got.G -} +var setup = got.Setup(nil) -func Test(t *testing.T) { - got.Each(t, T{}) -} +func TestTestLog(t *testing.T) { + g := setup(t) -func (t T) TestLog() { var res []interface{} lg := utils.Log(func(msg ...interface{}) { res = append(res, msg[0]) }) lg.Println("ok") - t.Eq(res[0], "ok") + g.Eq(res[0], "ok") utils.LoggerQuiet.Println() utils.MultiLogger(lg, lg).Println("ok") - t.Eq(res, []interface{}{"ok", "ok", "ok"}) + g.Eq(res, []interface{}{"ok", "ok", "ok"}) } -func (t T) TestE() { +func TestTestE(t *testing.T) { + g := setup(t) + utils.E(nil) - t.Panic(func() { + g.Panic(func() { utils.E(errors.New("err")) }) } -func (t T) STemplate() { +func TestSTemplate(t *testing.T) { + g := setup(t) + out := utils.S( "{{.a}} {{.b}} {{.c.A}} {{d}}", "a", "", @@ -53,22 +53,28 @@ func (t T) STemplate() { return "ok" }, ) - t.Eq(" 10 ok ok", out) + g.Eq(" 10 ok ok", out) } -func (t T) GenerateRandomString() { +func TestGenerateRandomString(t *testing.T) { + g := setup(t) + v := utils.RandString(10) raw, _ := hex.DecodeString(v) - t.Len(raw, 10) + g.Len(raw, 10) } -func (t T) Mkdir() { - p := filepath.Join(t.Testable.(*testing.T).TempDir(), "t") - t.E(utils.Mkdir(p)) +func TestMkdir(t *testing.T) { + g := setup(t) + + p := filepath.Join(g.Testable.(*testing.T).TempDir(), "t") + g.E(utils.Mkdir(p)) } -func (t T) OutputString() { - p := "tmp/" + t.Srand(16) +func TestOutputString(t *testing.T) { + g := setup(t) + + p := "tmp/" + g.RandStr(16) _ = utils.OutputFile(p, p) @@ -78,11 +84,13 @@ func (t T) OutputString() { panic(err) } - t.Eq(s, p) + g.Eq(s, p) } -func (t T) OutputBytes() { - p := "tmp/" + t.Srand(16) +func TestOutputBytes(t *testing.T) { + g := setup(t) + + p := "tmp/" + g.RandStr(16) _ = utils.OutputFile(p, []byte("test")) @@ -92,11 +100,13 @@ func (t T) OutputBytes() { panic(err) } - t.Eq(s, "test") + g.Eq(s, "test") } -func (t T) OutputStream() { - p := "tmp/" + t.Srand(16) +func TestOutputStream(t *testing.T) { + g := setup(t) + + p := "tmp/" + g.RandStr(16) b := bytes.NewBufferString("test") _ = utils.OutputFile(p, b) @@ -107,52 +117,66 @@ func (t T) OutputStream() { panic(err) } - t.Eq("test", s) + g.Eq("test", s) } -func (t T) OutputJSONErr() { - p := "tmp/" + t.Srand(16) +func TestOutputJSONErr(t *testing.T) { + g := setup(t) - t.Panic(func() { + p := "tmp/" + g.RandStr(16) + + g.Panic(func() { _ = utils.OutputFile(p, make(chan struct{})) }) } -func (t T) Sleep() { +func TestSleep(t *testing.T) { utils.Sleep(0.01) } -func (t T) All() { - c := t.Count(3) +func TestAll(t *testing.T) { + g := setup(t) + + c := g.Count(3) utils.All(c, c, c)() } -func (t T) Pause() { +func TestPause(t *testing.T) { go utils.Pause() } -func (t T) MustToJSON() { - t.Eq(utils.Dump("a", 10), `"a" 10`) - t.Eq(`{"a":1}`, utils.MustToJSON(map[string]int{"a": 1})) +func TestMustToJSON(t *testing.T) { + g := setup(t) + + g.Eq(utils.Dump("a", 10), `"a" 10`) + g.Eq(`{"a":1}`, utils.MustToJSON(map[string]int{"a": 1})) } -func (t T) FileExists() { - t.Eq(false, utils.FileExists(".")) - t.Eq(true, utils.FileExists("utils.go")) - t.Eq(false, utils.FileExists(t.Srand(16))) +func TestFileExists(t *testing.T) { + g := setup(t) + + g.Eq(false, utils.FileExists(".")) + g.Eq(true, utils.FileExists("utils.go")) + g.Eq(false, utils.FileExists(g.RandStr(16))) } -func (t T) ExecErr() { - t.Panic(func() { +func TestExecErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { utils.ExecLine("") }) } -func (t T) EscapeGoString() { - t.Eq("`` + \"`\" + `test` + \"`\" + ``", utils.EscapeGoString("`test`")) +func TestEscapeGoString(t *testing.T) { + g := setup(t) + + g.Eq("`` + \"`\" + `test` + \"`\" + ``", utils.EscapeGoString("`test`")) } -func (t T) IdleCounter() { +func TestIdleCounter(t *testing.T) { + g := setup(t) + utils.All(func() { ct := utils.NewIdleCounter(100 * time.Millisecond) @@ -164,15 +188,15 @@ func (t T) IdleCounter() { ct.Done() }() - ctx := t.Context() + ctx := g.Context() start := time.Now() ct.Wait(ctx) d := time.Since(start) - t.Gt(d, 400*time.Millisecond) - t.Lt(d, 450*time.Millisecond) + g.Gt(d, 400*time.Millisecond) + g.Lt(d, 450*time.Millisecond) - t.Panic(func() { + g.Panic(func() { ct.Done() }) @@ -181,26 +205,28 @@ func (t T) IdleCounter() { }, func() { ct := utils.NewIdleCounter(100 * time.Millisecond) start := time.Now() - ct.Wait(t.Context()) - t.Lt(time.Since(start), 150*time.Millisecond) + ct.Wait(g.Context()) + g.Lt(time.Since(start), 150*time.Millisecond) }, func() { ct := utils.NewIdleCounter(0) start := time.Now() - ct.Wait(t.Context()) - t.Lt(time.Since(start), 10*time.Millisecond) + ct.Wait(g.Context()) + g.Lt(time.Since(start), 10*time.Millisecond) })() } -func (t T) CropImage() { +func TestCropImage(t *testing.T) { + g := setup(t) + img := image.NewNRGBA(image.Rect(0, 0, 100, 100)) - t.Err(utils.CropImage(nil, 0, 0, 0, 0, 0)) + g.Err(utils.CropImage(nil, 0, 0, 0, 0, 0)) bin := bytes.NewBuffer(nil) - t.E(png.Encode(bin, img)) - t.E(utils.CropImage(bin.Bytes(), 0, 10, 10, 30, 30)) + g.E(png.Encode(bin, img)) + g.E(utils.CropImage(bin.Bytes(), 0, 10, 10, 30, 30)) bin = bytes.NewBuffer(nil) - t.E(jpeg.Encode(bin, img, &jpeg.Options{Quality: 80})) - t.E(utils.CropImage(bin.Bytes(), 0, 10, 10, 30, 30)) + g.E(jpeg.Encode(bin, img, &jpeg.Options{Quality: 80})) + g.E(utils.CropImage(bin.Bytes(), 0, 10, 10, 30, 30)) } diff --git a/must_test.go b/must_test.go index f4aedb5c..1fa57965 100644 --- a/must_test.go +++ b/must_test.go @@ -1,84 +1,92 @@ package rod_test import ( + "testing" + "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" ) -func (t T) BrowserWithPanic() { +func TestBrowserWithPanic(t *testing.T) { + g := setup(t) + var triggers int trigger := func(x interface{}) { triggers++ panic(x) } - browser := t.browser.Sleeper(rod.NotFoundSleeper).WithPanic(trigger) - t.Panic(func() { browser.MustPage("____") }) - t.Eq(1, triggers) + browser := g.browser.Sleeper(rod.NotFoundSleeper).WithPanic(trigger) + g.Panic(func() { browser.MustPage("____") }) + g.Eq(1, triggers) - page := browser.MustPage(t.blank()) + page := browser.MustPage(g.blank()) defer page.MustClose() - t.Panic(func() { page.MustElement("____") }) - t.Eq(2, triggers) + g.Panic(func() { page.MustElement("____") }) + g.Eq(2, triggers) el := page.MustElement("html") - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustClick() }) - t.Eq(3, triggers) + g.Eq(3, triggers) } -func (t T) PageWithPanic() { +func TestPageWithPanic(t *testing.T) { + g := setup(t) + var triggers int trigger := func(x interface{}) { triggers++ panic(x) } - browser := t.browser.Sleeper(rod.NotFoundSleeper) - t.Panic(func() { browser.MustPage("____") }) - t.Eq(0, triggers) + browser := g.browser.Sleeper(rod.NotFoundSleeper) + g.Panic(func() { browser.MustPage("____") }) + g.Eq(0, triggers) - page := browser.MustPage(t.blank()).WithPanic(trigger) + page := browser.MustPage(g.blank()).WithPanic(trigger) defer page.MustClose() - t.Panic(func() { page.MustElement("____") }) - t.Eq(1, triggers) + g.Panic(func() { page.MustElement("____") }) + g.Eq(1, triggers) el := page.MustElement("html") - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustClick() }) - t.Eq(2, triggers) + g.Eq(2, triggers) } -func (t T) ElementWithPanic() { +func TestElementWithPanic(t *testing.T) { + g := setup(t) + var triggers int trigger := func(x interface{}) { triggers++ panic(x) } - browser := t.browser.Sleeper(rod.NotFoundSleeper) - t.Panic(func() { browser.MustPage("____") }) - t.Eq(0, triggers) + browser := g.browser.Sleeper(rod.NotFoundSleeper) + g.Panic(func() { browser.MustPage("____") }) + g.Eq(0, triggers) - page := browser.MustPage(t.blank()) + page := browser.MustPage(g.blank()) defer page.MustClose() - t.Panic(func() { page.MustElement("____") }) - t.Eq(0, triggers) + g.Panic(func() { page.MustElement("____") }) + g.Eq(0, triggers) el := page.MustElement("html").WithPanic(trigger) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) el.MustClick() }) - t.Eq(1, triggers) + g.Eq(1, triggers) } diff --git a/page_eval_test.go b/page_eval_test.go index 125fc087..bb8519ed 100644 --- a/page_eval_test.go +++ b/page_eval_test.go @@ -1,6 +1,7 @@ package rod_test import ( + "testing" "time" "github.com/go-rod/rod" @@ -10,77 +11,87 @@ import ( "github.com/ysmood/gson" ) -func (t T) PageEvalOnNewDocument() { - p := t.newPage() +func TestPageEvalOnNewDocument(t *testing.T) { + g := setup(t) + + p := g.newPage() p.MustEvalOnNewDocument(`window.rod = 'ok'`) // to activate the script - p.MustNavigate(t.blank()) + p.MustNavigate(g.blank()) - t.Eq(p.MustEval("() => rod").String(), "ok") + g.Eq(p.MustEval("() => rod").String(), "ok") - t.Panic(func() { - t.mc.stubErr(1, proto.PageAddScriptToEvaluateOnNewDocument{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageAddScriptToEvaluateOnNewDocument{}) p.MustEvalOnNewDocument(`1`) }) } -func (t T) PageEval() { - page := t.page.MustNavigate(t.blank()) +func TestPageEval(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.blank()) - t.Eq(3, page.MustEval(` + g.Eq(3, page.MustEval(` (a, b) => a + b `, 1, 2).Int()) - t.Eq(page.MustEval(`function() { + g.Eq(page.MustEval(`function() { return 11 }`).Int(), 11) - t.Eq(page.MustEval(` ; () => 1; `).Int(), 1) + g.Eq(page.MustEval(` ; () => 1; `).Int(), 1) // reuse obj obj := page.MustEvaluate(rod.Eval(`() => () => 'ok'`).ByObject()) - t.Eq("ok", page.MustEval(`f => f()`, obj).Str()) + g.Eq("ok", page.MustEval(`f => f()`, obj).Str()) _, err := page.Eval(`10`) - t.Has(err.Error(), `eval js error: TypeError: 10.apply is not a function`) + g.Has(err.Error(), `eval js error: TypeError: 10.apply is not a function`) } -func (t T) PageEvaluateRetry() { - page := t.page.MustNavigate(t.blank()) +func TestPageEvaluateRetry(t *testing.T) { + g := setup(t) - t.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { - t.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { + page := g.page.MustNavigate(g.blank()) + + g.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { return gson.New(nil), cdp.ErrCtxNotFound }) return gson.New(nil), cdp.ErrCtxNotFound }) - t.Eq(1, page.MustEval(`() => 1`).Int()) + g.Eq(1, page.MustEval(`() => 1`).Int()) } -func (t T) PageUpdateJSCtxIDErr() { - page := t.page.MustNavigate(t.srcFile("./fixtures/click-iframe.html")) +func TestPageUpdateJSCtxIDErr(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.srcFile("./fixtures/click-iframe.html")) - t.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { - t.mc.stubErr(1, proto.RuntimeEvaluate{}) + g.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { + g.mc.stubErr(1, proto.RuntimeEvaluate{}) return gson.New(nil), cdp.ErrCtxNotFound }) - t.Err(page.Eval(`() => 1`)) + g.Err(page.Eval(`() => 1`)) frame := page.MustElement("iframe").MustFrame() frame.MustReload() - t.mc.stubErr(1, proto.DOMDescribeNode{}) - t.Err(frame.Element(`button`)) + g.mc.stubErr(1, proto.DOMDescribeNode{}) + g.Err(frame.Element(`button`)) frame.MustReload() - t.mc.stubErr(1, proto.DOMResolveNode{}) - t.Err(frame.Element(`button`)) + g.mc.stubErr(1, proto.DOMResolveNode{}) + g.Err(frame.Element(`button`)) } -func (t T) PageExpose() { - page := t.newPage(t.blank()).MustWaitLoad() +func TestPageExpose(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()).MustWaitLoad() stop := page.MustExpose("exposedFunc", func(g gson.JSON) (interface{}, error) { return g.Get("k").Str(), nil @@ -88,103 +99,115 @@ func (t T) PageExpose() { utils.All(func() { res := page.MustEval(`() => exposedFunc({k: 'a'})`) - t.Eq("a", res.Str()) + g.Eq("a", res.Str()) }, func() { res := page.MustEval(`() => exposedFunc({k: 'b'})`) - t.Eq("b", res.Str()) + g.Eq("b", res.Str()) })() // survive the reload page.MustReload().MustWaitLoad() res := page.MustEval(`() => exposedFunc({k: 'ok'})`) - t.Eq("ok", res.Str()) + g.Eq("ok", res.Str()) stop() - t.Panic(func() { + g.Panic(func() { stop() }) - t.Panic(func() { + g.Panic(func() { page.MustReload().MustWaitLoad().MustEval(`() => exposedFunc()`) }) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) page.MustExpose("exposedFunc", nil) }) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeAddBinding{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeAddBinding{}) page.MustExpose("exposedFunc2", nil) }) - t.Panic(func() { - t.mc.stubErr(1, proto.PageAddScriptToEvaluateOnNewDocument{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageAddScriptToEvaluateOnNewDocument{}) page.MustExpose("exposedFunc", nil) }) } -func (t T) Release() { - res, err := t.page.Evaluate(rod.Eval(`() => document`).ByObject()) - t.E(err) - t.page.MustRelease(res) +func TestRelease(t *testing.T) { + g := setup(t) + + res, err := g.page.Evaluate(rod.Eval(`() => document`).ByObject()) + g.E(err) + g.page.MustRelease(res) } -func (t T) PromiseLeak() { +func TestPromiseLeak(t *testing.T) { + g := setup(t) + /* Perform a slow action then navigate the page to another url, we can see the slow operation will still be executed. */ - p := t.page.MustNavigate(t.blank()) + p := g.page.MustNavigate(g.blank()) utils.All(func() { _, err := p.Eval(`() => new Promise(r => setTimeout(() => r(location.href), 1000))`) - t.Is(err, cdp.ErrCtxDestroyed) + g.Is(err, cdp.ErrCtxDestroyed) }, func() { utils.Sleep(0.3) - p.MustNavigate(t.blank()) + p.MustNavigate(g.blank()) })() } -func (t T) ObjectLeak() { +func TestObjectLeak(t *testing.T) { + g := setup(t) + /* Seems like it won't leak */ - p := t.page.MustNavigate(t.blank()) + p := g.page.MustNavigate(g.blank()) obj := p.MustEvaluate(rod.Eval("() => ({a:1})").ByObject()) p.MustReload().MustWaitLoad() - t.Panic(func() { + g.Panic(func() { p.MustEvaluate(rod.Eval(`obj => obj`, obj)) }) } -func (t T) PageObjectErr() { - t.Panic(func() { - t.page.MustObjectToJSON(&proto.RuntimeRemoteObject{ +func TestPageObjectErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { + g.page.MustObjectToJSON(&proto.RuntimeRemoteObject{ ObjectID: "not-exists", }) }) - t.Panic(func() { - t.page.MustElementFromNode(&proto.DOMNode{NodeID: -1}) + g.Panic(func() { + g.page.MustElementFromNode(&proto.DOMNode{NodeID: -1}) }) - t.Panic(func() { - node := t.page.MustNavigate(t.blank()).MustElement(`body`).MustDescribe() - t.mc.stubErr(1, proto.DOMResolveNode{}) - t.page.MustElementFromNode(node) + g.Panic(func() { + node := g.page.MustNavigate(g.blank()).MustElement(`body`).MustDescribe() + g.mc.stubErr(1, proto.DOMResolveNode{}) + g.page.MustElementFromNode(node) }) } -func (t T) GetJSHelperRetry() { - t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestGetJSHelperRetry(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/click.html")) - t.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.RuntimeCallFunctionOn{}, func(send StubSend) (gson.JSON, error) { return gson.JSON{}, cdp.ErrCtxNotFound }) - t.page.MustElements("button") + g.page.MustElements("button") } -func (t T) ConcurrentEval() { - p := t.page.MustNavigate(t.blank()) +func TestConcurrentEval(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.blank()) list := make(chan int, 2) start := time.Now() @@ -195,50 +218,60 @@ func (t T) ConcurrentEval() { })() duration := time.Since(start) - t.Lt(duration, 1500*time.Millisecond) - t.Gt(duration, 1000*time.Millisecond) - t.Eq([]int{<-list, <-list}, []int{1, 2}) + g.Lt(duration, 1500*time.Millisecond) + g.Gt(duration, 1000*time.Millisecond) + g.Eq([]int{<-list, <-list}, []int{1, 2}) } -func (t T) PageSlowRender() { - p := t.page.MustNavigate(t.srcFile("./fixtures/slow-render.html")) - t.Eq(p.MustElement("div").MustText(), "ok") +func TestPageSlowRender(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("./fixtures/slow-render.html")) + g.Eq(p.MustElement("div").MustText(), "ok") } -func (t T) PageIframeReload() { - p := t.page.MustNavigate(t.srcFile("./fixtures/click-iframe.html")) +func TestPageIframeReload(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("./fixtures/click-iframe.html")) frame := p.MustElement("iframe").MustFrame() btn := frame.MustElement("button") - t.Eq(btn.MustText(), "click me") + g.Eq(btn.MustText(), "click me") frame.MustReload() btn = frame.MustElement("button") - t.Eq(btn.MustText(), "click me") + g.Eq(btn.MustText(), "click me") - t.Has(*p.MustElement("iframe").MustAttribute("src"), "click.html") + g.Has(*p.MustElement("iframe").MustAttribute("src"), "click.html") } -func (t T) PageObjCrossNavigation() { - p := t.page.MustNavigate(t.blank()) +func TestPageObjCrossNavigation(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.blank()) obj := p.MustEvaluate(rod.Eval(`() => ({})`).ByObject()) - t.page.MustNavigate(t.blank()) + g.page.MustNavigate(g.blank()) _, err := p.Evaluate(rod.Eval(`() => 1`).This(obj)) - t.Is(err, &rod.ErrObjectNotFound{}) - t.Has(err.Error(), "cannot find object: {\"type\":\"object\"") + g.Is(err, &rod.ErrObjectNotFound{}) + g.Has(err.Error(), "cannot find object: {\"type\":\"object\"") } -func (t T) EnsureJSHelperErr() { - p := t.page.MustNavigate(t.blank()) +func TestEnsureJSHelperErr(t *testing.T) { + g := setup(t) - t.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) - t.Err(p.Elements(`button`)) + p := g.page.MustNavigate(g.blank()) + + g.mc.stubErr(2, proto.RuntimeCallFunctionOn{}) + g.Err(p.Elements(`button`)) } -func (t T) EvalOptionsString() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestEvalOptionsString(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustElement("button") - t.Eq(rod.Eval(`() => this.parentElement`).This(el.Object).String(), "() => this.parentElement() button") + g.Eq(rod.Eval(`() => this.parentElement`).This(el.Object).String(), "() => this.parentElement() button") } diff --git a/page_test.go b/page_test.go index 0486bf06..98df0af6 100644 --- a/page_test.go +++ b/page_test.go @@ -10,6 +10,7 @@ import ( "path/filepath" "sort" "sync" + "testing" "time" "github.com/go-rod/rod" @@ -21,19 +22,25 @@ import ( "github.com/ysmood/got" ) -func (t T) GetPageBrowser() { - t.Equal(t.page.Browser(), t.browser) +func TestGetPageBrowser(t *testing.T) { + g := setup(t) + + g.Equal(g.page.Browser(), g.browser) } -func (t T) GetPageURL() { - t.page.MustNavigate(t.srcFile("fixtures/click-iframe.html")).MustWaitLoad() - t.Regex(`/fixtures/click-iframe.html\z`, t.page.MustInfo().URL) +func TestGetPageURL(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/click-iframe.html")).MustWaitLoad() + g.Regex(`/fixtures/click-iframe.html\z`, g.page.MustInfo().URL) } -func (t T) SetCookies() { - s := t.Serve() +func TestSetCookies(t *testing.T) { + g := setup(t) - page := t.page.MustSetCookies([]*proto.NetworkCookieParam{{ + s := g.Serve() + + page := g.page.MustSetCookies([]*proto.NetworkCookieParam{{ Name: "cookie-a", Value: "1", URL: s.URL(), @@ -49,26 +56,28 @@ func (t T) SetCookies() { return cookies[i].Value < cookies[j].Value }) - t.Eq("1", cookies[0].Value) - t.Eq("2", cookies[1].Value) + g.Eq("1", cookies[0].Value) + g.Eq("2", cookies[1].Value) page.MustSetCookies() cookies = page.MustCookies() - t.Len(cookies, 0) + g.Len(cookies, 0) - t.Panic(func() { - t.mc.stubErr(1, proto.TargetGetTargetInfo{}) + g.Panic(func() { + g.mc.stubErr(1, proto.TargetGetTargetInfo{}) page.MustCookies() }) - t.Panic(func() { - t.mc.stubErr(1, proto.NetworkGetCookies{}) + g.Panic(func() { + g.mc.stubErr(1, proto.NetworkGetCookies{}) page.MustCookies() }) } -func (t T) SetExtraHeaders() { - s := t.Serve() +func TestSetExtraHeaders(t *testing.T) { + g := setup(t) + + s := g.Serve() wg := sync.WaitGroup{} var header http.Header @@ -77,15 +86,15 @@ func (t T) SetExtraHeaders() { wg.Done() }) - p := t.newPage() + p := g.newPage() cleanup := p.MustSetExtraHeaders("a", "1", "b", "2") wg.Add(1) p.MustNavigate(s.URL()) wg.Wait() - t.Eq(header.Get("a"), "1") - t.Eq(header.Get("b"), "2") + g.Eq(header.Get("a"), "1") + g.Eq(header.Get("b"), "2") cleanup() @@ -95,13 +104,15 @@ func (t T) SetExtraHeaders() { p.MustReload() wg.Wait() - t.Eq(header.Get("a"), "") - t.Eq(header.Get("b"), "") + g.Eq(header.Get("a"), "") + g.Eq(header.Get("b"), "") } } -func (t T) SetUserAgent() { - s := t.Serve() +func TestSetUserAgent(t *testing.T) { + g := setup(t) + + s := g.Serve() ua := "" lang := "" @@ -115,28 +126,34 @@ func (t T) SetUserAgent() { wg.Done() }) - t.newPage().MustSetUserAgent(nil).MustNavigate(s.URL()) + g.newPage().MustSetUserAgent(nil).MustNavigate(s.URL()) wg.Wait() - t.Eq(ua, "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36") - t.Eq(lang, "en") + g.Eq(ua, "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36") + g.Eq(lang, "en") } -func (t T) PageHTML() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")).MustWaitLoad() - t.Has(p.MustHTML(), "") +func TestPageHTML(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")).MustWaitLoad() + g.Has(p.MustHTML(), "") - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(p.HTML()) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(p.HTML()) } -func (t T) MustWaitElementsMoreThan() { - p := t.page.MustNavigate(t.srcFile("fixtures/wait_elements.html")).MustWaitElementsMoreThan("li", 5) - t.Gt(len(p.MustElements("li")), 5) +func TestMustWaitElementsMoreThan(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/wait_elements.html")).MustWaitElementsMoreThan("li", 5) + g.Gt(len(p.MustElements("li")), 5) } -func (t T) PageCloseCancel() { - page := t.browser.MustPage(t.srcFile("fixtures/prevent-close.html")) +func TestPageCloseCancel(t *testing.T) { + g := setup(t) + + page := g.browser.MustPage(g.srcFile("fixtures/prevent-close.html")) page.MustElement("body").MustClick() // only focused page will handle beforeunload event w, h := page.MustHandleDialog() @@ -144,32 +161,42 @@ func (t T) PageCloseCancel() { w() h(false, "") }() - t.Eq(page.Close().Error(), "page close canceled") + g.Eq(page.Close().Error(), "page close canceled") page.MustEval(`() => window.onbeforeunload = null`) page.MustClose() } -func (t T) LoadState() { - t.True(t.page.LoadState(&proto.PageEnable{})) +func TestLoadState(t *testing.T) { + g := setup(t) + + g.True(g.page.LoadState(&proto.PageEnable{})) } -func (t T) DisableDomain() { - defer t.page.DisableDomain(&proto.PageEnable{})() +func TestDisableDomain(t *testing.T) { + g := setup(t) + + defer g.page.DisableDomain(&proto.PageEnable{})() } -func (t T) PageContext() { - t.page.Timeout(time.Hour).CancelTimeout().MustEval(`() => 1`) +func TestPageContext(t *testing.T) { + g := setup(t) + + g.page.Timeout(time.Hour).CancelTimeout().MustEval(`() => 1`) } -func (t T) PageActivate() { - t.page.MustActivate() +func TestPageActivate(t *testing.T) { + g := setup(t) + + g.page.MustActivate() } -func (t T) Window() { - page := t.newPage(t.blank()) +func TestWindow(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()) - t.E(page.SetViewport(nil)) + g.E(page.SetViewport(nil)) bounds := page.MustGetWindow() defer page.MustSetWindow( @@ -186,37 +213,41 @@ func (t T) Window() { page.MustWindowMinimize() page.MustWindowNormal() page.MustSetWindow(0, 0, 1211, 611) - t.Eq(1211, page.MustEval(`() => window.innerWidth`).Int()) - t.Eq(611, page.MustEval(`() => window.innerHeight`).Int()) + g.Eq(1211, page.MustEval(`() => window.innerWidth`).Int()) + g.Eq(611, page.MustEval(`() => window.innerHeight`).Int()) - t.Panic(func() { - t.mc.stubErr(1, proto.BrowserGetWindowForTarget{}) + g.Panic(func() { + g.mc.stubErr(1, proto.BrowserGetWindowForTarget{}) page.MustGetWindow() }) - t.Panic(func() { - t.mc.stubErr(1, proto.BrowserGetWindowBounds{}) + g.Panic(func() { + g.mc.stubErr(1, proto.BrowserGetWindowBounds{}) page.MustGetWindow() }) - t.Panic(func() { - t.mc.stubErr(1, proto.BrowserGetWindowForTarget{}) + g.Panic(func() { + g.mc.stubErr(1, proto.BrowserGetWindowForTarget{}) page.MustSetWindow(0, 0, 1000, 1000) }) } -func (t T) SetViewport() { - page := t.newPage(t.blank()) +func TestSetViewport(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()) page.MustSetViewport(317, 419, 0, false) res := page.MustEval(`() => [window.innerWidth, window.innerHeight]`) - t.Eq(317, res.Get("0").Int()) - t.Eq(419, res.Get("1").Int()) + g.Eq(317, res.Get("0").Int()) + g.Eq(419, res.Get("1").Int()) - page2 := t.newPage(t.blank()) + page2 := g.newPage(g.blank()) res = page2.MustEval(`() => [window.innerWidth, window.innerHeight]`) - t.Neq(int(317), res.Get("0").Int()) + g.Neq(int(317), res.Get("0").Int()) } -func (t T) SetDocumentContent() { - page := t.newPage(t.blank()) +func TestSetDocumentContent(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()) doctype := "" html4StrictDoctype := `` @@ -226,112 +257,122 @@ func (t T) SetDocumentContent() { exampleWithHTML4StrictDoctype := html4StrictDoctype + "
test
" page.MustSetDocumentContent(exampleWithHTML4StrictDoctype) exp1 := page.MustEval(`() => new XMLSerializer().serializeToString(document)`).Str() - t.Eq(exp1, `
test
`) - t.Eq(page.MustElement("html").MustHTML(), "
test
") - t.Eq(page.MustElement("head").MustText(), "") + g.Eq(exp1, `
test
`) + g.Eq(page.MustElement("html").MustHTML(), "
test
") + g.Eq(page.MustElement("head").MustText(), "") exampleWithHTML4LooseDoctype := html4LooseDoctype + "
test
" page.MustSetDocumentContent(exampleWithHTML4LooseDoctype) exp2 := page.MustEval(`() => new XMLSerializer().serializeToString(document)`).Str() - t.Eq(exp2, `
test
`) - t.Eq(page.MustElement("html").MustHTML(), "
test
") - t.Eq(page.MustElement("head").MustText(), "") + g.Eq(exp2, `
test
`) + g.Eq(page.MustElement("html").MustHTML(), "
test
") + g.Eq(page.MustElement("head").MustText(), "") exampleWithXHTMLDoctype := xhtml11Doctype + "
test
" page.MustSetDocumentContent(exampleWithXHTMLDoctype) exp3 := page.MustEval(`() => new XMLSerializer().serializeToString(document)`).Str() - t.Eq(exp3, `
test
`) - t.Eq(page.MustElement("html").MustHTML(), "
test
") - t.Eq(page.MustElement("head").MustText(), "") + g.Eq(exp3, `
test
`) + g.Eq(page.MustElement("html").MustHTML(), "
test
") + g.Eq(page.MustElement("head").MustText(), "") exampleWithHTML5Doctype := doctype + "
test
" page.MustSetDocumentContent(exampleWithHTML5Doctype) exp4 := page.MustEval(`() => new XMLSerializer().serializeToString(document)`).Str() - t.Eq(exp4, `
test
`) - t.Eq(page.MustElement("html").MustHTML(), "
test
") - t.Eq(page.MustElement("head").MustText(), "") + g.Eq(exp4, `
test
`) + g.Eq(page.MustElement("html").MustHTML(), "
test
") + g.Eq(page.MustElement("head").MustText(), "") exampleWithoutDoctype := "
test
" page.MustSetDocumentContent(exampleWithoutDoctype) - t.Eq(page.MustElement("html").MustHTML(), "
test
") + g.Eq(page.MustElement("html").MustHTML(), "
test
") exampleBasic := doctype + "
test
" page.MustSetDocumentContent(exampleBasic) - t.Eq(page.MustElement("div").MustText(), "test") + g.Eq(page.MustElement("div").MustText(), "test") exampleWithTrickyContent := "
test
\x7F" page.MustSetDocumentContent(exampleWithTrickyContent) - t.Eq(page.MustElement("div").MustText(), "test") + g.Eq(page.MustElement("div").MustText(), "test") exampleWithEmoji := "
💪
" page.MustSetDocumentContent(exampleWithEmoji) - t.Eq(page.MustElement("div").MustText(), "💪") + g.Eq(page.MustElement("div").MustText(), "💪") } -func (t T) EmulateDevice() { - page := t.newPage(t.blank()) +func TestEmulateDevice(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()) page.MustEmulate(devices.IPhone6or7or8) res := page.MustEval(`() => [window.innerWidth, window.innerHeight, navigator.userAgent]`) // TODO: this seems like a bug of chromium { - t.Lt(math.Abs(float64(980-res.Get("0").Int())), 10) - t.Lt(math.Abs(float64(1743-res.Get("1").Int())), 10) + g.Lt(math.Abs(float64(980-res.Get("0").Int())), 10) + g.Lt(math.Abs(float64(1743-res.Get("1").Int())), 10) } - t.Eq( + g.Eq( "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1", res.Get("2").String(), ) - t.Panic(func() { - t.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) + g.Panic(func() { + g.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) page.MustEmulate(devices.IPad) }) - t.Panic(func() { - t.mc.stubErr(1, proto.EmulationSetTouchEmulationEnabled{}) + g.Panic(func() { + g.mc.stubErr(1, proto.EmulationSetTouchEmulationEnabled{}) page.MustEmulate(devices.IPad) }) } -func (t T) PageCloseErr() { - page := t.newPage(t.blank()) - t.Panic(func() { - t.mc.stubErr(1, proto.PageClose{}) +func TestPageCloseErr(t *testing.T) { + g := setup(t) + + page := g.newPage(g.blank()) + g.Panic(func() { + g.mc.stubErr(1, proto.PageClose{}) page.MustClose() }) } -func (t T) PageAddScriptTag() { - p := t.page.MustNavigate(t.blank()).MustWaitLoad() +func TestPageAddScriptTag(t *testing.T) { + g := setup(t) - res := p.MustAddScriptTag(t.srcFile("fixtures/add-script-tag.js")).MustEval(`() => count()`) - t.Eq(0, res.Int()) + p := g.page.MustNavigate(g.blank()).MustWaitLoad() - res = p.MustAddScriptTag(t.srcFile("fixtures/add-script-tag.js")).MustEval(`() => count()`) - t.Eq(1, res.Int()) + res := p.MustAddScriptTag(g.srcFile("fixtures/add-script-tag.js")).MustEval(`() => count()`) + g.Eq(0, res.Int()) - t.E(p.AddScriptTag("", `let ok = 'yes'`)) + res = p.MustAddScriptTag(g.srcFile("fixtures/add-script-tag.js")).MustEval(`() => count()`) + g.Eq(1, res.Int()) + + g.E(p.AddScriptTag("", `let ok = 'yes'`)) res = p.MustEval(`() => ok`) - t.Eq("yes", res.String()) + g.Eq("yes", res.String()) } -func (t T) PageAddStyleTag() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")).MustWaitLoad() +func TestPageAddStyleTag(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")).MustWaitLoad() - res := p.MustAddStyleTag(t.srcFile("fixtures/add-style-tag.css")). + res := p.MustAddStyleTag(g.srcFile("fixtures/add-style-tag.css")). MustElement("h4").MustEval(`() => getComputedStyle(this).color`) - t.Eq("rgb(255, 0, 0)", res.String()) + g.Eq("rgb(255, 0, 0)", res.String()) - p.MustAddStyleTag(t.srcFile("fixtures/add-style-tag.css")) - t.Len(p.MustElements("link"), 1) + p.MustAddStyleTag(g.srcFile("fixtures/add-style-tag.css")) + g.Len(p.MustElements("link"), 1) - t.E(p.AddStyleTag("", "h4 { color: green; }")) + g.E(p.AddStyleTag("", "h4 { color: green; }")) res = p.MustElement("h4").MustEval(`() => getComputedStyle(this).color`) - t.Eq("rgb(0, 128, 0)", res.String()) + g.Eq("rgb(0, 128, 0)", res.String()) } -func (t T) PageWaitOpen() { - page := t.page.MustNavigate(t.srcFile("fixtures/open-page.html")) +func TestPageWaitOpen(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.srcFile("fixtures/open-page.html")) wait := page.MustWaitOpen() @@ -340,39 +381,47 @@ func (t T) PageWaitOpen() { newPage := wait() defer newPage.MustClose() - t.Eq("new page", newPage.MustEval("() => window.a").String()) + g.Eq("new page", newPage.MustEval("() => window.a").String()) } -func (t T) PageWait() { - page := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestPageWait(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.srcFile("fixtures/click.html")) page.MustWait(`() => document.querySelector('button') !== null`) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) page.MustWait(``) }) } -func (t T) PageNavigateBlank() { - t.page.MustNavigate("") +func TestPageNavigateBlank(t *testing.T) { + g := setup(t) + + g.page.MustNavigate("") } -func (t T) PageWaitNavigation() { - s := t.Serve().Route("/", "") - wait := t.page.MustWaitNavigation() - t.page.MustNavigate(s.URL()) +func TestPageWaitNavigation(t *testing.T) { + g := setup(t) + + s := g.Serve().Route("/", "") + wait := g.page.MustWaitNavigation() + g.page.MustNavigate(s.URL()) wait() } -func (t T) PageWaitRequestIdle() { - s := t.Serve() +func TestPageWaitRequestIdle(t *testing.T) { + g := setup(t) + + s := g.Serve() sleep := time.Second s.Route("/r1", "") s.Mux.HandleFunc("/r2", func(w http.ResponseWriter, r *http.Request) { - t.E(w.Write([]byte("part"))) - ctx, cancel := context.WithTimeout(t.Context(), sleep) + g.E(w.Write([]byte("part"))) + ctx, cancel := context.WithTimeout(g.Context(), sleep) defer cancel() <-ctx.Done() }) @@ -383,7 +432,7 @@ func (t T) PageWaitRequestIdle() { s.Route("/r4", "") s.Route("/", ".html", ``) - page := t.newPage(s.URL()).MustWaitLoad() + page := g.newPage(s.URL()).MustWaitLoad() code := ` () => { fetch('/r2').then(r => r.text()) @@ -392,7 +441,7 @@ func (t T) PageWaitRequestIdle() { }` waitReq := "" - t.browser.Logger(utils.Log(func(msg ...interface{}) { + g.browser.Logger(utils.Log(func(msg ...interface{}) { typ := msg[0].(rod.TraceType) if typ == rod.TraceTypeWaitRequests { list := msg[2].(map[string]string) @@ -402,67 +451,77 @@ func (t T) PageWaitRequestIdle() { } } })) - defer t.browser.Logger(rod.DefaultLogger) + defer g.browser.Logger(rod.DefaultLogger) - t.browser.Trace(true) + g.browser.Trace(true) wait := page.MustWaitRequestIdle("/r1") - t.browser.Trace(defaults.Trace) + g.browser.Trace(defaults.Trace) page.MustEval(code) start := time.Now() wait() - t.Gt(time.Since(start), sleep) - t.Regex("/r2$", waitReq) + g.Gt(time.Since(start), sleep) + g.Regex("/r2$", waitReq) wait = page.MustWaitRequestIdle("/r2") page.MustEval(code) start = time.Now() wait() - t.Lt(time.Since(start), sleep) + g.Lt(time.Since(start), sleep) - t.Panic(func() { + g.Panic(func() { wait() }) } -func (t T) PageWaitIdle() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestPageWaitIdle(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) p.MustElement("button").MustClick() p.MustWaitIdle() - t.True(p.MustHas("[a=ok]")) + g.True(p.MustHas("[a=ok]")) } -func (t T) PageEventSession() { - s := t.Serve() - p := t.newPage(s.URL()) +func TestPageEventSession(t *testing.T) { + g := setup(t) + + s := g.Serve() + p := g.newPage(s.URL()) p.EnableDomain(proto.NetworkEnable{}) - go t.page.Context(t.Context()).EachEvent(func(e *proto.NetworkRequestWillBeSent) { - t.Log("should not goes to here") - t.Fail() + go g.page.Context(g.Context()).EachEvent(func(e *proto.NetworkRequestWillBeSent) { + g.Log("should not goes to here") + g.Fail() })() p.MustEval(`u => fetch(u)`, s.URL()) } -func (t T) PageWaitEvent() { - wait := t.page.WaitEvent(&proto.PageFrameNavigated{}) - t.page.MustNavigate(t.blank()) +func TestPageWaitEvent(t *testing.T) { + g := setup(t) + + wait := g.page.WaitEvent(&proto.PageFrameNavigated{}) + g.page.MustNavigate(g.blank()) wait() } -func (t T) PageWaitEventParseEventOnlyOnce() { - nav1 := t.page.WaitEvent(&proto.PageFrameNavigated{}) - nav2 := t.page.WaitEvent(&proto.PageFrameNavigated{}) - t.page.MustNavigate(t.blank()) +func TestPageWaitEventParseEventOnlyOnce(t *testing.T) { + g := setup(t) + + nav1 := g.page.WaitEvent(&proto.PageFrameNavigated{}) + nav2 := g.page.WaitEvent(&proto.PageFrameNavigated{}) + g.page.MustNavigate(g.blank()) nav1() nav2() } -func (t T) PageEvent() { - p := t.browser.MustPage() - ctx := t.Context() +func TestPageEvent(t *testing.T) { + g := setup(t) + + p := g.browser.MustPage() + ctx := g.Context() events := p.Context(ctx).Event() - p.MustNavigate(t.blank()) + p.MustNavigate(g.blank()) for msg := range events { if msg.Load(proto.PageFrameStartedLoading{}) { break @@ -478,8 +537,10 @@ func (t T) PageEvent() { p.MustClose() } -func (t T) PageStopEventAfterDetach() { - p := t.browser.MustPage().Context(t.Context()) +func TestPageStopEventAfterDetach(t *testing.T) { + g := setup(t) + + p := g.browser.MustPage().Context(g.Context()) go func() { utils.Sleep(0.3) p.MustClose() @@ -488,20 +549,24 @@ func (t T) PageStopEventAfterDetach() { } } -func (t T) Alert() { - page := t.page.MustNavigate(t.srcFile("fixtures/alert.html")) +func TestAlert(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.srcFile("fixtures/alert.html")) wait, handle := page.MustHandleDialog() go page.MustElement("button").MustClick() e := wait() - t.Eq(e.Message, "clicked") + g.Eq(e.Message, "clicked") handle(true, "") } -func (t T) Mouse() { - page := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestMouse(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.srcFile("fixtures/click.html")) page.MustElement("button") mouse := page.Mouse @@ -510,28 +575,30 @@ func (t T) Mouse() { mouse.MustDown("left") mouse.MustUp("left") - t.True(page.MustHas("[a=ok]")) + g.True(page.MustHas("[a=ok]")) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) mouse.MustScroll(0, 10) }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) mouse.MustDown(proto.InputMouseButtonLeft) }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) mouse.MustUp(proto.InputMouseButtonLeft) }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchMouseEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchMouseEvent{}) mouse.MustClick(proto.InputMouseButtonLeft) }) } -func (t T) MouseHoldMultiple() { - p := t.page.MustNavigate(t.blank()) +func TestMouseHoldMultiple(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.blank()) p.Mouse.MustDown("left") defer p.Mouse.MustUp("left") @@ -539,33 +606,37 @@ func (t T) MouseHoldMultiple() { defer p.Mouse.MustUp("right") } -func (t T) MouseClick() { - t.browser.SlowMotion(1) - defer func() { t.browser.SlowMotion(0) }() +func TestMouseClick(t *testing.T) { + g := setup(t) - page := t.page.MustNavigate(t.srcFile("fixtures/click.html")) + g.browser.SlowMotion(1) + defer func() { g.browser.SlowMotion(0) }() + + page := g.page.MustNavigate(g.srcFile("fixtures/click.html")) page.MustElement("button") mouse := page.Mouse mouse.MustMove(140, 160) mouse.MustClick("left") - t.True(page.MustHas("[a=ok]")) + g.True(page.MustHas("[a=ok]")) } -func (t T) MouseDrag() { - page := t.newPage().MustNavigate(t.srcFile("fixtures/drag.html")).MustWaitLoad() +func TestMouseDrag(t *testing.T) { + g := setup(t) + + page := g.newPage().MustNavigate(g.srcFile("fixtures/drag.html")).MustWaitLoad() mouse := page.Mouse mouse.MustMove(3, 3) mouse.MustDown("left") - t.E(mouse.Move(60, 80, 3)) + g.E(mouse.Move(60, 80, 3)) mouse.MustUp("left") utils.Sleep(0.3) - t.Eq(page.MustEval(`() => dragTrack`).Str(), " move 3 3 down 3 3 move 22 28 move 41 54 move 60 80 up 60 80") + g.Eq(page.MustEval(`() => dragTrack`).Str(), " move 3 3 down 3 3 move 22 28 move 41 54 move 60 80 up 60 80") } -func (t T) NativeDrag(got.Skip) { // devtools doesn't support to use mouse event to simulate it for now - page := t.page.MustNavigate(t.srcFile("fixtures/drag.html")) +func (g G) NativeDrag(got.Skip) { // devtools doesn't support to use mouse event to simulate it for now + page := g.page.MustNavigate(g.srcFile("fixtures/drag.html")) mouse := page.Mouse pt := page.MustElement("#draggable").MustShape().OnePointInside() @@ -576,18 +647,20 @@ func (t T) NativeDrag(got.Skip) { // devtools doesn't support to use mouse event mouse.MustMove(pt.X, pt.Y) mouse.MustDown("left") - t.E(mouse.Move(pt.X, toY, 5)) + g.E(mouse.Move(pt.X, toY, 5)) page.MustScreenshot("") mouse.MustUp("left") page.MustElement(".dropzone:nth-child(2) #draggable") } -func (t T) Touch() { - page := t.newPage().MustEmulate(devices.IPad) +func TestTouch(t *testing.T) { + g := setup(t) + + page := g.newPage().MustEmulate(devices.IPad) wait := page.WaitNavigation(proto.PageLifecycleEventNameLoad) - page.MustNavigate(t.srcFile("fixtures/touch.html")) + page.MustNavigate(g.srcFile("fixtures/touch.html")) wait() touch := page.Touch @@ -603,72 +676,80 @@ func (t T) Touch() { page.MustWait(`() => touchTrack == ' start 10 20 end start 30 40 end start 30 40 move 50 60 cancel'`) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchTouchEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchTouchEvent{}) touch.MustTap(1, 2) }) } -func (t T) PageScreenshot() { - f := filepath.Join("tmp", "screenshots", t.Srand(16)+".png") - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestPageScreenshot(t *testing.T) { + g := setup(t) + + f := filepath.Join("tmp", "screenshots", g.RandStr(16)+".png") + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) p.MustElement("button") p.MustScreenshot() data := p.MustScreenshot(f) img, err := png.Decode(bytes.NewBuffer(data)) - t.E(err) - t.Eq(1280, img.Bounds().Dx()) - t.Eq(800, img.Bounds().Dy()) - t.Nil(os.Stat(f)) + g.E(err) + g.Eq(1280, img.Bounds().Dx()) + g.Eq(800, img.Bounds().Dy()) + g.Nil(os.Stat(f)) p.MustScreenshot("") - t.Panic(func() { - t.mc.stubErr(1, proto.PageCaptureScreenshot{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageCaptureScreenshot{}) p.MustScreenshot() }) } -func (t T) ScreenshotFullPage() { - p := t.page.MustNavigate(t.srcFile("fixtures/scroll.html")) +func TestScreenshotFullPage(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/scroll.html")) p.MustElement("button") data := p.MustScreenshotFullPage() img, err := png.Decode(bytes.NewBuffer(data)) - t.E(err) + g.E(err) res := p.MustEval(`() => ({w: document.documentElement.scrollWidth, h: document.documentElement.scrollHeight})`) - t.Eq(res.Get("w").Int(), img.Bounds().Dx()) - t.Eq(res.Get("h").Int(), img.Bounds().Dy()) + g.Eq(res.Get("w").Int(), img.Bounds().Dx()) + g.Eq(res.Get("h").Int(), img.Bounds().Dy()) // after the full page screenshot the window size should be the same as before res = p.MustEval(`() => ({w: innerWidth, h: innerHeight})`) - t.Eq(1280, res.Get("w").Int()) - t.Eq(800, res.Get("h").Int()) + g.Eq(1280, res.Get("w").Int()) + g.Eq(800, res.Get("h").Int()) p.MustScreenshotFullPage() - noEmulation := t.newPage(t.blank()) - t.E(noEmulation.SetViewport(nil)) + noEmulation := g.newPage(g.blank()) + g.E(noEmulation.SetViewport(nil)) noEmulation.MustScreenshotFullPage() - t.Panic(func() { - t.mc.stubErr(1, proto.PageGetLayoutMetrics{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PageGetLayoutMetrics{}) p.MustScreenshotFullPage() }) - t.Panic(func() { - t.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) + g.Panic(func() { + g.mc.stubErr(1, proto.EmulationSetDeviceMetricsOverride{}) p.MustScreenshotFullPage() }) } -func (t T) ScreenshotFullPageInit() { - p := t.newPage(t.srcFile("fixtures/scroll.html")) +func TestScreenshotFullPageInit(t *testing.T) { + g := setup(t) + + p := g.newPage(g.srcFile("fixtures/scroll.html")) // should not panic p.MustScreenshotFullPage() } -func (t T) PageInput() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestPageInput(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("input") el.MustFocus() @@ -676,82 +757,94 @@ func (t T) PageInput() { p.Keyboard.MustInsertText(" Test") p.Keyboard.MustPress(input.Tab) - t.Eq("A Test", el.MustText()) + g.Eq("A Test", el.MustText()) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchKeyEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchKeyEvent{}) p.Keyboard.MustDown('a') }) - t.Panic(func() { - t.mc.stubErr(1, proto.InputDispatchKeyEvent{}) + g.Panic(func() { + g.mc.stubErr(1, proto.InputDispatchKeyEvent{}) p.Keyboard.MustUp('a') }) - t.Panic(func() { - t.mc.stubErr(3, proto.InputDispatchKeyEvent{}) + g.Panic(func() { + g.mc.stubErr(3, proto.InputDispatchKeyEvent{}) p.Keyboard.MustPress('a') }) } -func (t T) PageInputDate() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestPageInputDate(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) p.MustElement("[type=date]").MustInput("12") } -func (t T) PageScroll() { - p := t.page.MustNavigate(t.srcFile("fixtures/scroll.html")).MustWaitLoad() +func TestPageScroll(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/scroll.html")).MustWaitLoad() p.Mouse.MustMove(30, 30) p.Mouse.MustClick(proto.InputMouseButtonLeft) p.Mouse.MustScroll(0, 10) p.Mouse.MustScroll(100, 190) - t.E(p.Mouse.Scroll(200, 300, 5)) + g.E(p.Mouse.Scroll(200, 300, 5)) p.MustWait(`() => pageXOffset > 200 && pageYOffset > 300`) } -func (t T) PageConsoleLog() { - p := t.newPage(t.blank()).MustWaitLoad() +func TestPageConsoleLog(t *testing.T) { + g := setup(t) + + p := g.newPage(g.blank()).MustWaitLoad() e := &proto.RuntimeConsoleAPICalled{} wait := p.WaitEvent(e) p.MustEval(`() => console.log(1, {b: ['test']})`) wait() - t.Eq("test", p.MustObjectToJSON(e.Args[1]).Get("b.0").String()) - t.Eq(`1 map[b:[test]]`, p.MustObjectsToJSON(e.Args).Join(" ")) + g.Eq("test", p.MustObjectToJSON(e.Args[1]).Get("b.0").String()) + g.Eq(`1 map[b:[test]]`, p.MustObjectsToJSON(e.Args).Join(" ")) } -func (t T) Fonts() { +func TestFonts(t *testing.T) { + g := setup(t) + if !utils.InContainer { // No need to test font rendering on regular OS - t.SkipNow() + g.SkipNow() } - p := t.page.MustNavigate(t.srcFile("fixtures/fonts.html")).MustWaitLoad() + p := g.page.MustNavigate(g.srcFile("fixtures/fonts.html")).MustWaitLoad() p.MustPDF("tmp", "fonts.pdf") // download the file from Github Actions Artifacts } -func (t T) PagePDF() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestPagePDF(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) s, err := p.PDF(&proto.PagePrintToPDF{}) - t.E(err) - t.Nil(s.Close()) + g.E(err) + g.Nil(s.Close()) p.MustPDF("") - t.Panic(func() { - t.mc.stubErr(1, proto.PagePrintToPDF{}) + g.Panic(func() { + g.mc.stubErr(1, proto.PagePrintToPDF{}) p.MustPDF() }) } -func (t T) PageNavigateErr() { +func TestPageNavigateErr(t *testing.T) { + g := setup(t) + // dns error - err := t.page.Navigate("http://" + t.Srand(16)) - t.Is(err, &rod.ErrNavigation{}) - t.Is(err.Error(), "navigation failed: net::ERR_NAME_NOT_RESOLVED") + err := g.page.Navigate("http://" + g.RandStr(16)) + g.Is(err, &rod.ErrNavigation{}) + g.Is(err.Error(), "navigation failed: net::ERR_NAME_NOT_RESOLVED") - s := t.Serve() + s := g.Serve() s.Mux.HandleFunc("/404", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) @@ -761,54 +854,60 @@ func (t T) PageNavigateErr() { }) // will not panic - t.page.MustNavigate(s.URL("/404")) - t.page.MustNavigate(s.URL("/500")) + g.page.MustNavigate(s.URL("/404")) + g.page.MustNavigate(s.URL("/500")) - t.Panic(func() { - t.mc.stubErr(1, proto.PageStopLoading{}) - t.page.MustNavigate(t.blank()) + g.Panic(func() { + g.mc.stubErr(1, proto.PageStopLoading{}) + g.page.MustNavigate(g.blank()) }) - t.Panic(func() { - t.mc.stubErr(1, proto.PageNavigate{}) - t.page.MustNavigate(t.blank()) + g.Panic(func() { + g.mc.stubErr(1, proto.PageNavigate{}) + g.page.MustNavigate(g.blank()) }) } -func (t T) PageWaitLoadErr() { - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.page.MustWaitLoad() +func TestPageWaitLoadErr(t *testing.T) { + g := setup(t) + + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.page.MustWaitLoad() }) } -func (t T) PageNavigation() { - p := t.newPage().MustReload() +func TestPageNavigation(t *testing.T) { + g := setup(t) + + p := g.newPage().MustReload() wait := p.WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded) - p.MustNavigate(t.srcFile("fixtures/click.html")) + p.MustNavigate(g.srcFile("fixtures/click.html")) wait() wait = p.WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded) - p.MustNavigate(t.srcFile("fixtures/selector.html")) + p.MustNavigate(g.srcFile("fixtures/selector.html")) wait() wait = p.WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded) p.MustNavigateBack() wait() - t.Regex("fixtures/click.html$", p.MustInfo().URL) + g.Regex("fixtures/click.html$", p.MustInfo().URL) wait = p.WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded) p.MustNavigateForward() wait() - t.Regex("fixtures/selector.html$", p.MustInfo().URL) + g.Regex("fixtures/selector.html$", p.MustInfo().URL) - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(p.Reload()) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(p.Reload()) } -func (t T) PagePool() { +func TestPagePool(t *testing.T) { + g := setup(t) + pool := rod.NewPagePool(3) - create := func() *rod.Page { return t.browser.MustPage() } + create := func() *rod.Page { return g.browser.MustPage() } p := pool.Get(create) pool.Put(p) pool.Cleanup(func(p *rod.Page) { @@ -816,27 +915,31 @@ func (t T) PagePool() { }) } -func (t T) PageUseNonExistSession() { +func TestPageUseNonExistSession(t *testing.T) { + g := setup(t) + // TODO: chrome bug that hangs for closing non-exist session id // Related chrome ticket: https://bugs.chromium.org/p/chromium/issues/detail?id=1151822 - p := t.browser.PageFromSession("nonexist").Timeout(300 * time.Millisecond) + p := g.browser.PageFromSession("nonexist").Timeout(300 * time.Millisecond) err := proto.PageClose{}.Call(p) - t.Is(err, context.DeadlineExceeded) + g.Is(err, context.DeadlineExceeded) } -func (t T) PageElementFromObjectErr() { - p := t.newPage() +func TestPageElementFromObjectErr(t *testing.T) { + g := setup(t) + + p := g.newPage() wait := p.WaitNavigation(proto.PageLifecycleEventNameLoad) - p.MustNavigate(t.srcFile("./fixtures/click.html")) + p.MustNavigate(g.srcFile("./fixtures/click.html")) wait() res, err := proto.DOMGetNodeForLocation{X: 10, Y: 10}.Call(p) - t.E(err) + g.E(err) obj, err := proto.DOMResolveNode{ BackendNodeID: res.BackendNodeID, }.Call(p) - t.E(err) + g.E(err) - t.mc.stubErr(1, proto.RuntimeEvaluate{}) - t.Err(p.ElementFromObject(obj.Object)) + g.mc.stubErr(1, proto.RuntimeEvaluate{}) + g.Err(p.ElementFromObject(obj.Object)) } diff --git a/query_test.go b/query_test.go index ec0dcfb1..83f695bb 100644 --- a/query_test.go +++ b/query_test.go @@ -3,6 +3,7 @@ package rod_test import ( "context" "errors" + "testing" "time" "github.com/go-rod/rod" @@ -13,82 +14,92 @@ import ( "github.com/ysmood/gson" ) -func (t T) PageElements() { - t.page.MustNavigate(t.srcFile("fixtures/input.html")) - t.page.MustElement("input") - list := t.page.MustElements("input") - t.Eq("input", list.First().MustDescribe().LocalName) - t.Eq("submit", list.Last().MustText()) +func TestPageElements(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/input.html")) + g.page.MustElement("input") + list := g.page.MustElements("input") + g.Eq("input", list.First().MustDescribe().LocalName) + g.Eq("submit", list.Last().MustText()) } -func (t T) Pages() { - t.page.MustNavigate(t.srcFile("fixtures/click.html")).MustWaitLoad() - pages := t.browser.MustPages() +func TestPages(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/click.html")).MustWaitLoad() + pages := g.browser.MustPages() - t.True(pages.MustFind("button").MustHas("button")) - t.Panic(func() { rod.Pages{}.MustFind("____") }) - t.True(pages.MustFindByURL("click.html").MustHas("button")) - t.Panic(func() { rod.Pages{}.MustFindByURL("____") }) + g.True(pages.MustFind("button").MustHas("button")) + g.Panic(func() { rod.Pages{}.MustFind("____") }) + g.True(pages.MustFindByURL("click.html").MustHas("button")) + g.Panic(func() { rod.Pages{}.MustFindByURL("____") }) _, err := pages.Find("____") - t.Err(err) - t.Eq(err.Error(), "cannot find page") - t.Panic(func() { + g.Err(err) + g.Eq(err.Error(), "cannot find page") + g.Panic(func() { pages.MustFindByURL("____") }) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) pages.MustFind("button") }) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) pages.MustFindByURL("____") }) } -func (t T) PageHas() { - t.page.MustNavigate(t.srcFile("fixtures/selector.html")) - t.page.MustElement("body") - t.True(t.page.MustHas("span")) - t.False(t.page.MustHas("a")) - t.True(t.page.MustHasX("//span")) - t.False(t.page.MustHasX("//a")) - t.True(t.page.MustHasR("button", "03")) - t.False(t.page.MustHasR("button", "11")) - - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(t.page.HasX("//a")) - - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(t.page.HasR("button", "03")) +func TestPageHas(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/selector.html")) + g.page.MustElement("body") + g.True(g.page.MustHas("span")) + g.False(g.page.MustHas("a")) + g.True(g.page.MustHasX("//span")) + g.False(g.page.MustHasX("//a")) + g.True(g.page.MustHasR("button", "03")) + g.False(g.page.MustHasR("button", "11")) + + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(g.page.HasX("//a")) + + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(g.page.HasR("button", "03")) } -func (t T) ElementHas() { - t.page.MustNavigate(t.srcFile("fixtures/selector.html")) - b := t.page.MustElement("body") - t.True(b.MustHas("span")) - t.False(b.MustHas("a")) - t.True(b.MustHasX("//span")) - t.False(b.MustHasX("//a")) - t.True(b.MustHasR("button", "03")) - t.False(b.MustHasR("button", "11")) +func TestElementHas(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/selector.html")) + b := g.page.MustElement("body") + g.True(b.MustHas("span")) + g.False(b.MustHas("a")) + g.True(b.MustHasX("//span")) + g.False(b.MustHasX("//a")) + g.True(b.MustHasR("button", "03")) + g.False(b.MustHasR("button", "11")) } -func (t T) Search() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestSearch(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) el := p.MustSearch("click me") - t.Eq("click me", el.MustText()) - t.True(el.MustClick().MustMatches("[a=ok]")) + g.Eq("click me", el.MustText()) + g.True(el.MustClick().MustMatches("[a=ok]")) _, err := p.Sleeper(rod.NotFoundSleeper).Search("not-exists") - t.True(errors.Is(err, &rod.ErrElementNotFound{})) - t.Eq(err.Error(), "cannot find element") + g.True(errors.Is(err, &rod.ErrElementNotFound{})) + g.Eq(err.Error(), "cannot find element") // when search result is not ready { - t.mc.stub(1, proto.DOMGetSearchResults{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.DOMGetSearchResults{}, func(send StubSend) (gson.JSON, error) { return gson.New(nil), cdp.ErrCtxNotFound }) p.MustSearch("click me") @@ -96,7 +107,7 @@ func (t T) Search() { // when node id is zero { - t.mc.stub(1, proto.DOMGetSearchResults{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.DOMGetSearchResults{}, func(send StubSend) (gson.JSON, error) { return gson.New(proto.DOMGetSearchResultsResult{ NodeIds: []proto.DOMNodeID{0}, }), nil @@ -104,93 +115,103 @@ func (t T) Search() { p.MustSearch("click me") } - t.Panic(func() { - t.mc.stubErr(1, proto.DOMPerformSearch{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMPerformSearch{}) p.MustSearch("click me") }) - t.Panic(func() { - t.mc.stubErr(1, proto.DOMGetSearchResults{}) + g.Panic(func() { + g.mc.stubErr(1, proto.DOMGetSearchResults{}) p.MustSearch("click me") }) - t.Panic(func() { - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Panic(func() { + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) p.MustSearch("click me") }) } -func (t T) SearchElements() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestSearchElements(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) { res, err := p.Search("button") - t.E(err) + g.E(err) c, err := res.All() - t.E(err) + g.E(err) - t.Len(c, 4) + g.Len(c, 4) - t.mc.stubErr(1, proto.DOMGetSearchResults{}) - t.Err(res.All()) + g.mc.stubErr(1, proto.DOMGetSearchResults{}) + g.Err(res.All()) - t.mc.stubErr(1, proto.DOMResolveNode{}) - t.Err(res.All()) + g.mc.stubErr(1, proto.DOMResolveNode{}) + g.Err(res.All()) } { // disable retry sleeper := func() utils.Sleeper { return utils.CountSleeper(1) } _, err := p.Sleeper(sleeper).Search("not-exists") - t.Err(err) + g.Err(err) } } -func (t T) SearchIframes() { - p := t.page.MustNavigate(t.srcFile("fixtures/click-iframes.html")) +func TestSearchIframes(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click-iframes.html")) el := p.MustSearch("button[onclick]") - t.Eq("click me", el.MustText()) - t.True(el.MustClick().MustMatches("[a=ok]")) + g.Eq("click me", el.MustText()) + g.True(el.MustClick().MustMatches("[a=ok]")) } -func (t T) SearchIframesAfterReload() { - p := t.page.MustNavigate(t.srcFile("fixtures/click-iframes.html")) +func TestSearchIframesAfterReload(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click-iframes.html")) frame := p.MustElement("iframe").MustFrame().MustElement("iframe").MustFrame() frame.MustReload() el := p.MustSearch("button[onclick]") - t.Eq("click me", el.MustText()) - t.True(el.MustClick().MustMatches("[a=ok]")) + g.Eq("click me", el.MustText()) + g.True(el.MustClick().MustMatches("[a=ok]")) } -func (t T) PageRace() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestPageRace(t *testing.T) { + g := setup(t) - p.Race().Element("button").MustHandle(func(e *rod.Element) { t.Eq("01", e.MustText()) }).MustDo() - t.Eq("01", p.Race().Element("button").MustDo().MustText()) + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) - p.Race().ElementX("//button").MustHandle(func(e *rod.Element) { t.Eq("01", e.MustText()) }).MustDo() - t.Eq("01", p.Race().ElementX("//button").MustDo().MustText()) + p.Race().Element("button").MustHandle(func(e *rod.Element) { g.Eq("01", e.MustText()) }).MustDo() + g.Eq("01", p.Race().Element("button").MustDo().MustText()) - p.Race().ElementR("button", "02").MustHandle(func(e *rod.Element) { t.Eq("02", e.MustText()) }).MustDo() - t.Eq("02", p.Race().ElementR("button", "02").MustDo().MustText()) + p.Race().ElementX("//button").MustHandle(func(e *rod.Element) { g.Eq("01", e.MustText()) }).MustDo() + g.Eq("01", p.Race().ElementX("//button").MustDo().MustText()) + + p.Race().ElementR("button", "02").MustHandle(func(e *rod.Element) { g.Eq("02", e.MustText()) }).MustDo() + g.Eq("02", p.Race().ElementR("button", "02").MustDo().MustText()) p.Race().MustElementByJS("() => document.querySelector('button')", nil). - MustHandle(func(e *rod.Element) { t.Eq("01", e.MustText()) }).MustDo() - t.Eq("01", p.Race().MustElementByJS("() => document.querySelector('button')", nil).MustDo().MustText()) + MustHandle(func(e *rod.Element) { g.Eq("01", e.MustText()) }).MustDo() + g.Eq("01", p.Race().MustElementByJS("() => document.querySelector('button')", nil).MustDo().MustText()) el, err := p.Sleeper(func() utils.Sleeper { return utils.CountSleeper(2) }).Race(). Element("not-exists").MustHandle(func(e *rod.Element) {}). ElementX("//not-exists"). ElementR("not-exists", "test").MustHandle(func(e *rod.Element) {}). Do() - t.Err(err) - t.Nil(el) + g.Err(err) + g.Nil(el) el, err = p.Race().MustElementByJS(`() => notExists()`, nil).Do() - t.Err(err) - t.Nil(el) + g.Err(err) + g.Nil(el) } -func (t T) PageRaceRetryInHandle() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestPageRaceRetryInHandle(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) p.Race().Element("div").MustHandle(func(e *rod.Element) { go func() { utils.Sleep(0.5) @@ -200,162 +221,196 @@ func (t T) PageRaceRetryInHandle() { }).MustDo() } -func (t T) PageElementX() { - t.page.MustNavigate(t.srcFile("fixtures/click.html")) - t.page.MustElement("body") - name := t.page.MustElementX("//*[contains(text(), 'click')]").MustDescribe().LocalName - t.Eq("button", name) +func TestPageElementX(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/click.html")) + g.page.MustElement("body") + name := g.page.MustElementX("//*[contains(text(), 'click')]").MustDescribe().LocalName + g.Eq("button", name) } -func (t T) PageElementsX() { - t.page.MustNavigate(t.srcFile("fixtures/selector.html")) - t.page.MustElement("body") - list := t.page.MustElementsX("//button") - t.Len(list, 4) +func TestPageElementsX(t *testing.T) { + g := setup(t) + + g.page.MustNavigate(g.srcFile("fixtures/selector.html")) + g.page.MustElement("body") + list := g.page.MustElementsX("//button") + g.Len(list, 4) } -func (t T) ElementR() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestElementR(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) el := p.MustElementR("button", `\d1`) - t.Eq("01", el.MustText()) + g.Eq("01", el.MustText()) el = p.MustElement("div").MustElementR("button", `03`) - t.Eq("03", el.MustText()) + g.Eq("03", el.MustText()) - p = t.page.MustNavigate(t.srcFile("fixtures/input.html")) + p = g.page.MustNavigate(g.srcFile("fixtures/input.html")) el = p.MustElementR("input", `submit`) - t.Eq("submit", el.MustText()) + g.Eq("submit", el.MustText()) el = p.MustElementR("input", `placeholder`) - t.Eq("blur", *el.MustAttribute("id")) + g.Eq("blur", *el.MustAttribute("id")) el = p.MustElementR("option", `/cc/i`) - t.Eq("CC", el.MustText()) + g.Eq("CC", el.MustText()) } -func (t T) ElementFromElement() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestElementFromElement(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) el := p.MustElement("div").MustElement("button") - t.Eq("02", el.MustText()) + g.Eq("02", el.MustText()) } -func (t T) ElementsFromElement() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestElementsFromElement(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("form") list := p.MustElement("form").MustElements("option") - t.Len(list, 4) - t.Eq("B", list[1].MustText()) + g.Len(list, 4) + g.Eq("B", list[1].MustText()) - t.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) - t.Err(el.Elements("input")) + g.mc.stubErr(1, proto.RuntimeCallFunctionOn{}) + g.Err(el.Elements("input")) } -func (t T) ElementParent() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) +func TestElementParent(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) el := p.MustElement("input").MustParent() - t.Eq("FORM", el.MustEval(`() => this.tagName`).String()) + g.Eq("FORM", el.MustEval(`() => this.tagName`).String()) } -func (t T) ElementParents() { - p := t.page.MustNavigate(t.srcFile("fixtures/input.html")) - t.Len(p.MustElement("option").MustParents("*"), 4) - t.Len(p.MustElement("option").MustParents("form"), 1) +func TestElementParents(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/input.html")) + g.Len(p.MustElement("option").MustParents("*"), 4) + g.Len(p.MustElement("option").MustParents("form"), 1) } -func (t T) ElementSiblings() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestElementSiblings(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) el := p.MustElement("div") a := el.MustPrevious() b := el.MustNext() - t.Eq(a.MustText(), "01") - t.Eq(b.MustText(), "04") + g.Eq(a.MustText(), "01") + g.Eq(b.MustText(), "04") } -func (t T) ElementFromElementX() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestElementFromElementX(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) el := p.MustElement("div").MustElementX("./button") - t.Eq("02", el.MustText()) + g.Eq("02", el.MustText()) } -func (t T) ElementsFromElementsX() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")) +func TestElementsFromElementsX(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")) list := p.MustElement("div").MustElementsX("./button") - t.Len(list, 2) + g.Len(list, 2) } -func (t T) ElementTracing() { - t.browser.Trace(true) - t.browser.Logger(utils.LoggerQuiet) +func TestElementTracing(t *testing.T) { + g := setup(t) + + g.browser.Trace(true) + g.browser.Logger(utils.LoggerQuiet) defer func() { - t.browser.Trace(defaults.Trace) - t.browser.Logger(rod.DefaultLogger) + g.browser.Trace(defaults.Trace) + g.browser.Logger(rod.DefaultLogger) }() - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) - t.Eq(`rod.element("code") html`, p.MustElement("html").MustElement("code").MustText()) + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) + g.Eq(`rod.element("code") html`, p.MustElement("html").MustElement("code").MustText()) } -func (t T) PageElementByJS() { - p := t.page.MustNavigate(t.srcFile("fixtures/click.html")) +func TestPageElementByJS(t *testing.T) { + g := setup(t) + + p := g.page.MustNavigate(g.srcFile("fixtures/click.html")) - t.Eq(p.MustElementByJS(`() => document.querySelector('button')`).MustText(), "click me") + g.Eq(p.MustElementByJS(`() => document.querySelector('button')`).MustText(), "click me") _, err := p.ElementByJS(rod.Eval(`() => 1`)) - t.Is(err, &rod.ErrExpectElement{}) - t.Eq(err.Error(), "expect js to return an element, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") + g.Is(err, &rod.ErrExpectElement{}) + g.Eq(err.Error(), "expect js to return an element, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") } -func (t T) PageElementsByJS() { - p := t.page.MustNavigate(t.srcFile("fixtures/selector.html")).MustWaitLoad() +func TestPageElementsByJS(t *testing.T) { + g := setup(t) - t.Len(p.MustElementsByJS("() => document.querySelectorAll('button')"), 4) + p := g.page.MustNavigate(g.srcFile("fixtures/selector.html")).MustWaitLoad() + + g.Len(p.MustElementsByJS("() => document.querySelectorAll('button')"), 4) _, err := p.ElementsByJS(rod.Eval(`() => [1]`)) - t.Is(err, &rod.ErrExpectElements{}) - t.Eq(err.Error(), "expect js to return an array of elements, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") + g.Is(err, &rod.ErrExpectElements{}) + g.Eq(err.Error(), "expect js to return an array of elements, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") _, err = p.ElementsByJS(rod.Eval(`() => 1`)) - t.Eq(err.Error(), "expect js to return an array of elements, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") + g.Eq(err.Error(), "expect js to return an array of elements, but got: {\"type\":\"number\",\"value\":1,\"description\":\"1\"}") _, err = p.ElementsByJS(rod.Eval(`() => foo()`)) - t.Err(err) + g.Err(err) - t.mc.stubErr(1, proto.RuntimeGetProperties{}) + g.mc.stubErr(1, proto.RuntimeGetProperties{}) _, err = p.ElementsByJS(rod.Eval(`() => [document.body]`)) - t.Err(err) + g.Err(err) - t.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) - t.Err(p.Elements("button")) + g.mc.stubErr(4, proto.RuntimeCallFunctionOn{}) + g.Err(p.Elements("button")) } -func (t T) PageElementTimeout() { - page := t.page.MustNavigate(t.blank()) +func TestPageElementTimeout(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.blank()) start := time.Now() _, err := page.Timeout(300 * time.Millisecond).Element("not-exists") - t.Is(err, context.DeadlineExceeded) - t.Gte(time.Since(start), 300*time.Millisecond) + g.Is(err, context.DeadlineExceeded) + g.Gte(time.Since(start), 300*time.Millisecond) } -func (t T) PageElementMaxRetry() { - page := t.page.MustNavigate(t.blank()) +func TestPageElementMaxRetry(t *testing.T) { + g := setup(t) + + page := g.page.MustNavigate(g.blank()) s := func() utils.Sleeper { return utils.CountSleeper(5) } _, err := page.Sleeper(s).Element("not-exists") - t.Is(err, &utils.ErrMaxSleepCount{}) + g.Is(err, &utils.ErrMaxSleepCount{}) } -func (t T) ElementsOthers() { +func TestElementsOthers(t *testing.T) { + g := setup(t) + list := rod.Elements{} - t.Nil(list.First()) - t.Nil(list.Last()) + g.Nil(list.First()) + g.Nil(list.Last()) } -func (t T) PagesOthers() { +func TestPagesOthers(t *testing.T) { + g := setup(t) + list := rod.Pages{} - t.Nil(list.First()) - t.Nil(list.Last()) + g.Nil(list.First()) + g.Nil(list.Last()) list = append(list, &rod.Page{}) - t.NotNil(list.First()) - t.NotNil(list.Last()) + g.NotNil(list.First()) + g.NotNil(list.Last()) } diff --git a/setup_test.go b/setup_test.go index 23fe0657..203bd1e7 100644 --- a/setup_test.go +++ b/setup_test.go @@ -22,7 +22,7 @@ import ( "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/ysmood/got" - "github.com/ysmood/gotrace/pkg/testleak" + "github.com/ysmood/gotrace" "github.com/ysmood/gson" ) @@ -38,15 +38,29 @@ func init() { launcher.NewBrowser().MustGet() // preload browser to local } -// entry point for all tests -func Test(t *testing.T) { - testleak.Check(t, 0) +var testerPool TesterPool - got.Each(t, newTesterPool(t).get) +func TestMain(m *testing.M) { + testerPool = newTesterPool() + + code := m.Run() + if code != 0 { + os.Exit(code) + } + + testerPool.cleanup() + + if err := gotrace.Check(0); err != nil { + log.Fatal(err) + } } -// T is a tester. Testers are thread-safe, they shouldn't race each other. -type T struct { +var setup = func(t *testing.T) G { + return testerPool.get(t) +} + +// G is a tester. Testers are thread-safe, they shouldn't race each other. +type G struct { got.G mc *MockClient @@ -54,36 +68,32 @@ type T struct { page *rod.Page } -type TesterPool chan *T +type TesterPool struct { + pool chan *G + parallel int +} -func newTesterPool(t *testing.T) TesterPool { +func newTesterPool() TesterPool { parallel := got.Parallel() if parallel == 0 { parallel = runtime.GOMAXPROCS(0) } fmt.Println("parallel test", parallel) - cp := TesterPool(make(chan *T, parallel)) - - t.Cleanup(func() { - go func() { - for i := 0; i < parallel; i++ { - if t := <-cp; t != nil { - t.browser.MustClose() - } - } - }() - }) + cp := TesterPool{ + pool: make(chan *G, parallel), + parallel: parallel, + } for i := 0; i < parallel; i++ { - cp <- nil + cp.pool <- nil } return cp } // new tester -func (cp TesterPool) new() *T { +func (tp TesterPool) new() *G { u := launcher.New().MustLaunch() mc := newMockClient(u) @@ -92,7 +102,7 @@ func (cp TesterPool) new() *T { page := browser.MustPage() - return &T{ + return &G{ mc: mc, browser: browser, page: page, @@ -100,17 +110,17 @@ func (cp TesterPool) new() *T { } // get a tester -func (cp TesterPool) get(t *testing.T) T { - parallel := got.Parallel() != 1 +func (tp TesterPool) get(t *testing.T) G { + parallel := tp.parallel > 1 if parallel { t.Parallel() } - tester := <-cp + tester := <-tp.pool if tester == nil { - tester = cp.new() + tester = tp.new() } - t.Cleanup(func() { cp <- tester }) + t.Cleanup(func() { tp.pool <- tester }) tester.G = got.New(t) tester.mc.t = t @@ -122,61 +132,69 @@ func (cp TesterPool) get(t *testing.T) T { return *tester } -func (t T) enableCDPLog() { - t.mc.principal.Logger(rod.DefaultLogger) +func (tp TesterPool) cleanup() { + for i := 0; i < tp.parallel; i++ { + if t := <-testerPool.pool; t != nil { + t.browser.MustClose() + } + } +} + +func (g G) enableCDPLog() { + g.mc.principal.Logger(rod.DefaultLogger) } -func (t T) dump(args ...interface{}) { - t.Log(utils.Dump(args)) +func (g G) dump(args ...interface{}) { + g.Log(utils.Dump(args)) } -func (t T) blank() string { - return t.srcFile("./fixtures/blank.html") +func (g G) blank() string { + return g.srcFile("./fixtures/blank.html") } // Get abs file path from fixtures folder, such as "file:///a/b/click.html". // Usually the path can be used for html src attribute like: // -func (t T) srcFile(path string) string { - t.Helper() +func (g G) srcFile(path string) string { + g.Helper() f, err := filepath.Abs(slash(path)) - t.E(err) + g.E(err) return "file://" + f } -func (t T) newPage(u ...string) *rod.Page { - t.Helper() - p := t.browser.MustPage(u...) - t.Cleanup(func() { - if !t.Failed() { +func (g G) newPage(u ...string) *rod.Page { + g.Helper() + p := g.browser.MustPage(u...) + g.Cleanup(func() { + if !g.Failed() { p.MustClose() } }) return p } -func (t T) checkLeaking(checkGoroutine bool) { +func (g G) checkLeaking(checkGoroutine bool) { if checkGoroutine { - testleak.Check(t.Testable.(*testing.T), 0) + gotrace.CheckTest(g.Testable, 0) } - t.Cleanup(func() { - if t.Failed() { + g.Cleanup(func() { + if g.Failed() { return } - res, err := proto.TargetGetTargets{}.Call(t.browser) - t.E(err) + res, err := proto.TargetGetTargets{}.Call(g.browser) + g.E(err) if len(res.TargetInfos) > 2 { // don't account the init about:blank - t.Logf("leaking pages: %v", utils.Dump(res.TargetInfos)) + g.Logf("leaking pages: %v", utils.Dump(res.TargetInfos)) } - if t.browser.LoadState(t.page.SessionID, proto.FetchEnable{}) { - t.Logf("leaking FetchEnable") - t.FailNow() + if g.browser.LoadState(g.page.SessionID, proto.FetchEnable{}) { + g.Logf("leaking FetchEnable") + g.FailNow() } - t.mc.setCall(nil) + g.mc.setCall(nil) }) } @@ -338,9 +356,9 @@ func (mr *MockReader) Read(p []byte) (n int, err error) { return 0, mr.err } -func (t T) LintIgnore(got.Skip) { +func (g G) LintIgnore(got.Skip) { _ = rod.Try(func() { - tt := T{} + tt := G{} tt.dump() tt.enableCDPLog()