This commit is contained in:
2024-09-12 08:57:44 +10:00
commit eb10ca9ca3
35 changed files with 1354 additions and 0 deletions

237
e2e/e2e_test.go Normal file
View File

@@ -0,0 +1,237 @@
//go:build e2e
package e2e_test
import (
"bufio"
"database/sql"
"fmt"
"log"
"math/rand"
"net/url"
"os"
"os/exec"
"syscall"
"testing"
"time"
"github.com/playwright-community/playwright-go"
_ "github.com/tursodatabase/libsql-client-go/libsql"
_ "modernc.org/sqlite"
)
// global variables, can be used in any tests
var (
pw *playwright.Playwright
browser playwright.Browser
context playwright.BrowserContext
page playwright.Page
expect playwright.PlaywrightAssertions
isChromium bool
isFirefox bool
isWebKit bool
browserName = getBrowserName()
browserType playwright.BrowserType
app *exec.Cmd
baseUrL *url.URL
)
// defaultContextOptions for most tests
var defaultContextOptions = playwright.BrowserNewContextOptions{
AcceptDownloads: playwright.Bool(true),
HasTouch: playwright.Bool(true),
}
func TestMain(m *testing.M) {
beforeAll()
code := m.Run()
afterAll()
os.Exit(code)
}
// beforeAll prepares the environment, including
// - start Playwright driver
// - launch browser depends on BROWSER env
// - init web-first assertions, alias as `expect`
func beforeAll() {
err := playwright.Install()
if err != nil {
log.Fatalf("could not install Playwright: %v", err)
}
pw, err = playwright.Run()
if err != nil {
log.Fatalf("could not start Playwright: %v", err)
}
if browserName == "chromium" || browserName == "" {
browserType = pw.Chromium
} else if browserName == "firefox" {
browserType = pw.Firefox
} else if browserName == "webkit" {
browserType = pw.WebKit
}
// launch browser, headless or not depending on HEADFUL env
browser, err = browserType.Launch(playwright.BrowserTypeLaunchOptions{
Headless: playwright.Bool(os.Getenv("HEADFUL") == ""),
})
if err != nil {
log.Fatalf("could not launch: %v", err)
}
// init web-first assertions with 1s timeout instead of default 5s
expect = playwright.NewPlaywrightAssertions(1000)
isChromium = browserName == "chromium" || browserName == ""
isFirefox = browserName == "firefox"
isWebKit = browserName == "webkit"
// start app
if err = startApp(); err != nil {
log.Fatalf("could not start app: %v", err)
}
time.Sleep(time.Second * 5)
if err = seedDB(); err != nil {
log.Fatalf("could not seed db: %v", err)
}
}
func startApp() error {
port := getPort()
app = exec.Command("go", "run", "main.go")
app.Dir = "../"
app.Env = append(
os.Environ(),
"DB_URL=./test-db.sqlite3",
fmt.Sprintf("PORT=%d", port),
"LOG_LEVEL=DEBUG",
)
var err error
baseUrL, err = url.Parse(fmt.Sprintf("http://localhost:%d", port))
if err != nil {
return err
}
stdout, err := app.StdoutPipe()
if err != nil {
return err
}
stderr, err := app.StderrPipe()
if err != nil {
return err
}
if err := app.Start(); err != nil {
return err
}
fmt.Printf("Started app on port %d, pid %d", port, app.Process.Pid)
stdoutchan := make(chan string)
stderrchan := make(chan string)
go func() {
defer close(stdoutchan)
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
stdoutchan <- scanner.Text()
}
}()
go func() {
defer close(stderrchan)
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
stderrchan <- scanner.Text()
}
}()
go func() {
for line := range stdoutchan {
fmt.Println("[STDOUT]", line)
}
}()
go func() {
for line := range stderrchan {
fmt.Println("[STDERR]", line)
}
}()
return nil
}
func seedDB() error {
db, err := sql.Open("libsql", "file:../test-db.sqlite3")
if err != nil {
return err
}
b, err := os.ReadFile("./testdata/seed.sql")
if err != nil {
return err
}
_, err = db.Exec(string(b))
if err != nil {
return err
}
return nil
}
func getPort() int {
randomGenerator := rand.New(rand.NewSource(time.Now().UnixNano()))
return randomGenerator.Intn(9001-3000) + 3000
}
// afterAll does cleanup, e.g. stop playwright driver
func afterAll() {
if app != nil && app.Process != nil {
if err := syscall.Kill(-app.Process.Pid, syscall.SIGKILL); err != nil {
fmt.Println(err)
}
}
if err := pw.Stop(); err != nil {
log.Fatalf("could not start Playwright: %v", err)
}
if err := os.Remove("../test-db.sqlite3"); err != nil {
log.Fatalf("could not remove test-db.sqlite3: %v", err)
}
}
// beforeEach creates a new context and page for each test,
// so each test has isolated environment. Usage:
//
// Func TestFoo(t *testing.T) {
// beforeEach(t)
// // your test code
// }
func beforeEach(t *testing.T, contextOptions ...playwright.BrowserNewContextOptions) {
t.Helper()
opt := defaultContextOptions
if len(contextOptions) == 1 {
opt = contextOptions[0]
}
context, page = newBrowserContextAndPage(t, opt)
}
func getBrowserName() string {
browserName, hasEnv := os.LookupEnv("BROWSER")
if hasEnv {
return browserName
}
return "chromium"
}
func newBrowserContextAndPage(t *testing.T, options playwright.BrowserNewContextOptions) (playwright.BrowserContext, playwright.Page) {
t.Helper()
context, err := browser.NewContext(options)
if err != nil {
t.Fatalf("could not create new context: %v", err)
}
t.Cleanup(func() {
if err := context.Close(); err != nil {
t.Errorf("could not close context: %v", err)
}
})
p, err := context.NewPage()
if err != nil {
t.Fatalf("could not create new page: %v", err)
}
return context, p
}
func getFullPath(relativePath string) string {
return baseUrL.ResolveReference(&url.URL{Path: relativePath}).String()
}

16
e2e/home_test.go Normal file
View File

@@ -0,0 +1,16 @@
//go:build e2e
package e2e_test
import (
"github.com/stretchr/testify/require"
"testing"
)
func TestHome(t *testing.T) {
beforeEach(t)
_, err := page.Goto(getFullPath(""))
require.NoError(t, err)
require.NoError(t, expect.Locator(page.GetByText("Welcome!")).ToBeVisible())
}

1
e2e/testdata/seed.sql vendored Normal file
View File

@@ -0,0 +1 @@
-- TODO: fill this what you need for E2E testing