mirror of
https://github.com/playwright-community/playwright-go.git
synced 2026-06-03 21:02:27 +08:00
Some checks failed
Go / Lint (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, oldstable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, oldstable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, oldstable, windows-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, stable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, stable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (chromium, stable, windows-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, oldstable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, oldstable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, oldstable, windows-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, stable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, stable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (firefox, stable, windows-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, oldstable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, oldstable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, oldstable, windows-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, stable, macos-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, stable, ubuntu-latest) (push) Has been cancelled
Go / ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} (webkit, stable, windows-latest) (push) Has been cancelled
Go / test-examples (push) Has been cancelled
Docs / Deploy docs (push) Has been cancelled
Verify Types / verify (push) Has been cancelled
Go / finish (push) Has been cancelled
* chore: roll to Playwright v1.57.0 Fix playwright submodule commit Rerun gofumpt Change ConsoleMessages to use newConsoleMessage() to properly deserialize event objects Add test coverage Update page_test.go Fix failing end-to-end test Update README.md Fix README Update README.md * Fix unit tests
310 lines
8.8 KiB
Go
310 lines
8.8 KiB
Go
package playwright_test
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/playwright-community/playwright-go"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBrowserContextOutputTrace(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Start(playwright.TracingStartOptions{
|
|
Screenshots: playwright.Bool(true),
|
|
Snapshots: playwright.Bool(true),
|
|
}))
|
|
|
|
_, err := page.Goto(server.PREFIX + "/grid.html")
|
|
require.NoError(t, err)
|
|
dir := t.TempDir()
|
|
err = context.Tracing().Stop(filepath.Join(dir, "trace.zip"))
|
|
require.NoError(t, err)
|
|
require.FileExists(t, filepath.Join(dir, "trace.zip"))
|
|
}
|
|
|
|
func TestTracingStartStop(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Start())
|
|
require.NoError(t, context.Tracing().Stop())
|
|
}
|
|
|
|
func TestBrowserContextShouldNoErrorWhenStoppingWithoutStart(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Stop())
|
|
}
|
|
|
|
func TestBrowserContextOutputTraceChunk(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Start(playwright.TracingStartOptions{
|
|
Screenshots: playwright.Bool(true),
|
|
Snapshots: playwright.Bool(true),
|
|
}))
|
|
|
|
_, err := page.Goto(server.PREFIX + "/grid.html")
|
|
require.NoError(t, err)
|
|
dir := t.TempDir()
|
|
|
|
button := page.Locator(".box").First()
|
|
|
|
err = context.Tracing().StartChunk(playwright.TracingStartChunkOptions{
|
|
Title: playwright.String("foo"),
|
|
})
|
|
require.NoError(t, err)
|
|
err = button.Click()
|
|
require.NoError(t, err)
|
|
err = context.Tracing().StopChunk(filepath.Join(dir, "trace1.zip"))
|
|
require.NoError(t, err)
|
|
require.FileExists(t, filepath.Join(dir, "trace1.zip"))
|
|
|
|
err = context.Tracing().StartChunk(playwright.TracingStartChunkOptions{
|
|
Title: playwright.String("foo"),
|
|
})
|
|
require.NoError(t, err)
|
|
err = button.Click()
|
|
require.NoError(t, err)
|
|
err = context.Tracing().StopChunk(filepath.Join(dir, "trace2.zip"))
|
|
require.NoError(t, err)
|
|
require.FileExists(t, filepath.Join(dir, "trace2.zip"))
|
|
}
|
|
|
|
func TestBrowserContextTracingOutputMultipleChunks(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Start(playwright.TracingStartOptions{
|
|
Screenshots: playwright.Bool(true),
|
|
Snapshots: playwright.Bool(true),
|
|
}))
|
|
|
|
_, err := page.Goto(server.PREFIX + "/frames/frame.html")
|
|
require.NoError(t, err)
|
|
require.NoError(t, context.Tracing().StartChunk())
|
|
require.NoError(t, page.SetContent("<button>Click</button>"))
|
|
require.NoError(t, page.Locator("button").Click())
|
|
dir := t.TempDir()
|
|
require.NoError(t, context.Tracing().StopChunk(filepath.Join(dir, "trace.zip")))
|
|
require.FileExists(t, filepath.Join(dir, "trace.zip"))
|
|
}
|
|
|
|
func TestBrowserContextTracingRemoteConnect(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
remoteServer, err := newRemoteServer()
|
|
require.NoError(t, err)
|
|
defer remoteServer.Close()
|
|
|
|
browser1, err := browserType.Connect(remoteServer.url)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, browser1)
|
|
defer browser1.Close()
|
|
|
|
context1, err := browser1.NewContext()
|
|
require.NoError(t, err)
|
|
require.NoError(t, context1.Tracing().Start(playwright.TracingStartOptions{
|
|
Screenshots: playwright.Bool(true),
|
|
Snapshots: playwright.Bool(true),
|
|
}))
|
|
page1, err := context1.NewPage()
|
|
require.NoError(t, err)
|
|
_, err = page1.Goto(server.PREFIX + "/frames/frame.html")
|
|
require.NoError(t, err)
|
|
require.NoError(t, context1.Tracing().StartChunk())
|
|
require.NoError(t, page1.SetContent("<button>Click</button>"))
|
|
require.NoError(t, page1.Locator("button").Click())
|
|
dir := t.TempDir()
|
|
require.NoError(t, context1.Tracing().StopChunk(filepath.Join(dir, "trace.zip")))
|
|
require.FileExists(t, filepath.Join(dir, "trace.zip"))
|
|
}
|
|
|
|
func TestShouldShowTracingGroupInActionList(t *testing.T) {
|
|
BeforeEach(t)
|
|
|
|
require.NoError(t, context.Tracing().Start())
|
|
page, err := context.NewPage()
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, context.Tracing().Group("outer group"))
|
|
_, err = page.Goto(`data:text/html,<!DOCTYPE html><body><div>Hello world</div></body>`)
|
|
require.NoError(t, err)
|
|
require.NoError(t, context.Tracing().Group("inner group 1"))
|
|
require.NoError(t, page.Locator("body").Click())
|
|
require.NoError(t, context.Tracing().GroupEnd())
|
|
require.NoError(t, context.Tracing().Group("inner group 2"))
|
|
visiable, err := page.GetByText("Hello").IsVisible()
|
|
require.NoError(t, err)
|
|
require.True(t, visiable)
|
|
require.NoError(t, context.Tracing().GroupEnd())
|
|
require.NoError(t, context.Tracing().GroupEnd())
|
|
|
|
tracePath := filepath.Join(t.TempDir(), "trace.zip")
|
|
require.NoError(t, context.Tracing().Stop(tracePath))
|
|
require.FileExists(t, tracePath)
|
|
|
|
_, events := parseTrace(t, tracePath)
|
|
actions := getTraceActions(events)
|
|
require.Equal(t,
|
|
[]string{
|
|
"BrowserContext.NewPage",
|
|
"outer group",
|
|
"Page.Goto",
|
|
"inner group 1",
|
|
"Locator.Click",
|
|
"inner group 2",
|
|
"Locator.IsVisible",
|
|
}, actions)
|
|
}
|
|
|
|
// mapInternalAPIToPublic maps internal Playwright class.method names to public API names
|
|
func mapInternalAPIToPublic(class, method string) string {
|
|
// Map internal classes to public API classes
|
|
classMethodKey := class + "." + method
|
|
|
|
// Common Frame methods that should map to Page
|
|
frameToPageMethods := map[string]bool{
|
|
"goto": true, "reload": true, "goBack": true, "goForward": true,
|
|
"setContent": true, "waitForNavigation": true, "waitForURL": true,
|
|
"waitForLoadState": true, "screenshot": true, "pdf": true,
|
|
"close": true, "pause": true,
|
|
}
|
|
|
|
// Frame selector/locator methods that should map to Locator
|
|
frameLocatorMethods := map[string]bool{
|
|
"click": true, "dblclick": true, "fill": true, "press": true,
|
|
"type": true, "hover": true, "check": true, "uncheck": true,
|
|
"selectOption": true, "setInputFiles": true, "focus": true,
|
|
"blur": true, "tap": true, "dispatchEvent": true, "evaluate": true,
|
|
"isVisible": true, "isHidden": true, "isEnabled": true, "isDisabled": true,
|
|
"isChecked": true, "isEditable": true, "textContent": true,
|
|
"innerText": true, "innerHTML": true, "getAttribute": true,
|
|
}
|
|
|
|
if class == "Frame" {
|
|
if frameToPageMethods[method] {
|
|
class = "Page"
|
|
} else if frameLocatorMethods[method] {
|
|
class = "Locator"
|
|
}
|
|
}
|
|
|
|
// Special case mappings
|
|
specialMappings := map[string]string{
|
|
"BrowserContext.newPage": "BrowserContext.NewPage",
|
|
"Page.waitForTimeout": "Page.WaitForTimeout",
|
|
}
|
|
|
|
// Convert method to title case (first letter uppercase)
|
|
titleCaseMethod := strings.ToUpper(method[:1]) + method[1:]
|
|
apiName := class + "." + titleCaseMethod
|
|
|
|
// Check for special mappings
|
|
if mapped, ok := specialMappings[classMethodKey]; ok {
|
|
return mapped
|
|
}
|
|
if mapped, ok := specialMappings[apiName]; ok {
|
|
return mapped
|
|
}
|
|
|
|
return apiName
|
|
}
|
|
|
|
func parseTrace(t *testing.T, tracePath string) (files map[string][]byte, events []interface{}) {
|
|
t.Helper()
|
|
// read and unzip trace
|
|
r, err := zip.OpenReader(tracePath)
|
|
require.NoError(t, err)
|
|
defer r.Close()
|
|
|
|
files = make(map[string][]byte)
|
|
events = make([]interface{}, 0)
|
|
actionMap := make(map[string]interface{})
|
|
for _, f := range r.File {
|
|
rc, err := f.Open()
|
|
require.NoError(t, err)
|
|
defer rc.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
_, err = io.Copy(buf, rc)
|
|
require.NoError(t, err)
|
|
|
|
files[f.Name] = buf.Bytes()
|
|
if f.Name == "trace.trace" || f.Name == "trace.network" {
|
|
// read lines
|
|
for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) {
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
|
|
var event map[string]interface{}
|
|
err := json.Unmarshal(line, &event)
|
|
require.NoError(t, err)
|
|
eventType, _ := event["type"].(string)
|
|
|
|
switch eventType {
|
|
case "before":
|
|
event["type"] = "action"
|
|
// Compute apiName from class and method for regular actions
|
|
// For tracing groups, use the title field
|
|
class, _ := event["class"].(string)
|
|
method, _ := event["method"].(string)
|
|
title, hasTitle := event["title"].(string)
|
|
|
|
if method == "tracingGroup" && hasTitle {
|
|
event["apiName"] = title
|
|
} else if class != "" && method != "" {
|
|
event["apiName"] = mapInternalAPIToPublic(class, method)
|
|
}
|
|
|
|
actionMap[event["callId"].(string)] = event
|
|
events = append(events, event)
|
|
case "input":
|
|
break
|
|
case "after":
|
|
break
|
|
default:
|
|
events = append(events, event)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func getTraceActions(events []interface{}) []string {
|
|
actions := make([]string, 0)
|
|
actionEvents := slices.DeleteFunc(events, func(e interface{}) bool {
|
|
event := e.(map[string]interface{})
|
|
return event["type"].(string) != "action"
|
|
})
|
|
slices.SortFunc(actionEvents, func(a, b interface{}) int {
|
|
eventA := a.(map[string]interface{})
|
|
eventB := b.(map[string]interface{})
|
|
t1 := eventA["startTime"].(float64)
|
|
t2 := eventB["startTime"].(float64)
|
|
if t1 < t2 {
|
|
return -1
|
|
}
|
|
if t1 > t2 {
|
|
return 1
|
|
}
|
|
return 0
|
|
})
|
|
for _, e := range actionEvents {
|
|
event := e.(map[string]interface{})
|
|
if apiName, ok := event["apiName"].(string); ok {
|
|
actions = append(actions, apiName)
|
|
}
|
|
}
|
|
return actions
|
|
}
|