Documentation
¶
Overview ¶
Package testing provides a comprehensive framework for end-to-end testing of LiveTemplate applications.
Quick Start ¶
import lvttest "github.com/livetemplate/lvt/testing"
func TestHomePage(t *testing.T) {
test := lvttest.Setup(t, &lvttest.SetupOptions{
AppPath: "./main.go",
})
defer test.Cleanup()
test.Navigate("/")
assert := lvttest.NewAssert(test)
assert.PageContains("Welcome")
}
Features ¶
- Automatic Chrome/Chromium management (Docker or local)
- Automatic server startup and shutdown
- WebSocket connection handling
- Console log capture (browser, server, WebSocket)
- CRUD operation helpers
- Modal testing
- Search, sort, and pagination testing
- Database seeding and cleanup
- Resource testing for lvt-generated apps
Chrome Modes ¶
The framework supports three Chrome modes:
- Docker (default) - Uses chromedp/headless-shell container
- Local - Uses locally installed Chrome/Chromium
- Shared - Uses shared Chrome instance from TestMain
See Setup() and SetupOptions for configuration.
Code Reduction ¶
This framework dramatically reduces e2e test boilerplate:
- Before: ~100-150 lines per CRUD test
- After: ~10-20 lines per CRUD test
- Reduction: 85-90%
Examples ¶
See examples/testing/ directory for complete examples:
- 01_basic - Simple smoke test
- 02_crud - Full CRUD workflow
- 03_debugging - Console log capture
- 04_assertions - All assertion types
- 05_modal - Modal interactions
- 06_interactions - Search, sort, pagination
- 07_database - Database seeding
- 08_resource - One-liner resource testing
- 09_parallel - Parallel testing with shared Chrome
Package testing provides test utilities for LiveTemplate applications.
HTTPTest provides a lightweight alternative to browser-based E2E tests. It starts the test server and makes HTTP requests directly, without requiring a browser. This is ideal for testing:
- Server-side rendering
- Form submission and validation
- CRUD operations
- API responses
- Template expression validation
For tests that require browser JavaScript execution (WebSocket, focus management, animations), use the browser-based E2ETest instead.
Index ¶
- func CleanupAllTestContainers()
- func CleanupChromeContainers()
- func CleanupTestContainers()
- func GenerateTestAppName(prefix string) string
- func GetAWSCredentials() (accessKeyID, secretAccessKey, bucket, region string, err error)
- func GetChromeTestURL(port int) string
- func GetClientLibraryJS() []byte
- func GetFlyAPIToken() (string, error)
- func GetFreePort() (port int, err error)
- func HasCredentials(provider Provider) bool
- func IsTestAppName(name string) bool
- func RequireAWSCredentials(t *stdtesting.T)
- func RequireCredentials(t *stdtesting.T, provider Provider)
- func RequireFlyCredentials(t *stdtesting.T)
- func ServeClientLibrary(w http.ResponseWriter, r *http.Request)
- func SetupUpdateEventListener() chromedp.Action
- func StartDockerChrome(t *testing.T, debugPort int) error
- func StartTestServer(t *testing.T, mainPath string, port int) *exec.Cmd
- func StopDockerChrome(t *testing.T, debugPort int)
- func ValidateAppName(name string) error
- func ValidateCredentials(provider Provider) error
- func ValidateNoTemplateExpressions(selector string) chromedp.Action
- func WaitFor(condition string, timeout time.Duration) chromedp.Action
- func WaitForActionResponse(actionName string, timeout time.Duration) chromedp.Action
- func WaitForCount(selector string, expectedCount int, timeout time.Duration) chromedp.Action
- func WaitForMessageCount(expectedCount int, timeout time.Duration) chromedp.Action
- func WaitForText(selector, text string, timeout time.Duration) chromedp.Action
- func WaitForUpdateEvent(actionName string, timeout time.Duration) chromedp.Action
- func WaitForWebSocketReady(timeout time.Duration) chromedp.Action
- type Assert
- func (a *Assert) AttributeValue(selector, attribute, expectedValue string) error
- func (a *Assert) ElementCount(selector string, expectedCount int) error
- func (a *Assert) ElementExists(selector string) error
- func (a *Assert) ElementHidden(selector string) error
- func (a *Assert) ElementNotExists(selector string) error
- func (a *Assert) ElementVisible(selector string) error
- func (a *Assert) FormFieldValue(selector, expectedValue string) error
- func (a *Assert) HasClass(selector, className string) error
- func (a *Assert) NoConsoleErrors() error
- func (a *Assert) NoTemplateErrors() error
- func (a *Assert) NotHasClass(selector, className string) error
- func (a *Assert) PageContains(text string) error
- func (a *Assert) PageNotContains(text string) error
- func (a *Assert) TableRowCount(expectedCount int) error
- func (a *Assert) TextContains(selector, expectedSubstring string) error
- func (a *Assert) TextContent(selector, expectedText string) error
- func (a *Assert) WebSocketConnected() error
- type CRUDTester
- func (c *CRUDTester) Create(fields ...Field) error
- func (c *CRUDTester) Delete(recordID string) error
- func (c *CRUDTester) Edit(recordID string, fields ...Field) error
- func (c *CRUDTester) GetTableRows() ([]map[string]string, error)
- func (c *CRUDTester) VerifyExists(searchText string) error
- func (c *CRUDTester) VerifyNotExists(searchText string) error
- type ChromeMode
- type ConsoleLog
- type ConsoleLogger
- func (cl *ConsoleLogger) Clear()
- func (cl *ConsoleLogger) Count() int
- func (cl *ConsoleLogger) CountByType(logType string) int
- func (cl *ConsoleLogger) FilterByType(logType string) []ConsoleLog
- func (cl *ConsoleLogger) FindLog(pattern string) (ConsoleLog, bool)
- func (cl *ConsoleLogger) GetErrors() []ConsoleLog
- func (cl *ConsoleLogger) GetLogs() []ConsoleLog
- func (cl *ConsoleLogger) GetWarnings() []ConsoleLog
- func (cl *ConsoleLogger) HasErrors() bool
- func (cl *ConsoleLogger) HasWarnings() bool
- func (cl *ConsoleLogger) Print()
- func (cl *ConsoleLogger) PrintErrors()
- func (cl *ConsoleLogger) Start(ctx context.Context)
- type DatabaseAssert
- func (d *DatabaseAssert) RecordCount(t *testing.T, table string, expected int)
- func (d *DatabaseAssert) RecordDeleted(t *testing.T, table string, id interface{})
- func (d *DatabaseAssert) RecordExists(t *testing.T, table string, conditions map[string]interface{})
- func (d *DatabaseAssert) RecordNotExists(t *testing.T, table string, conditions map[string]interface{})
- type DeploymentOptions
- type DeploymentTest
- type E2ETest
- type Field
- type HTTPAssert
- func (a *HTTPAssert) Contains(t *testing.T, text string)
- func (a *HTTPAssert) ContainsAll(t *testing.T, texts ...string)
- func (a *HTTPAssert) ContentType(t *testing.T, expected string)
- func (a *HTTPAssert) ContentTypeHTML(t *testing.T)
- func (a *HTTPAssert) ContentTypeJSON(t *testing.T)
- func (a *HTTPAssert) ElementCount(t *testing.T, selector string, expected int)
- func (a *HTTPAssert) ElementText(t *testing.T, selector string, expectedText string)
- func (a *HTTPAssert) ElementTextContains(t *testing.T, selector string, expectedSubstring string)
- func (a *HTTPAssert) FormFieldValue(t *testing.T, fieldName string, expectedValue string)
- func (a *HTTPAssert) HasCSRFToken(t *testing.T)
- func (a *HTTPAssert) HasElement(t *testing.T, selector string)
- func (a *HTTPAssert) HasFormField(t *testing.T, fieldName string)
- func (a *HTTPAssert) HasHeader(t *testing.T, name string)
- func (a *HTTPAssert) HasNoElement(t *testing.T, selector string)
- func (a *HTTPAssert) Header(t *testing.T, name, expected string)
- func (a *HTTPAssert) Matches(t *testing.T, pattern string)
- func (a *HTTPAssert) NoTemplateErrors(t *testing.T)
- func (a *HTTPAssert) NotContains(t *testing.T, text string)
- func (a *HTTPAssert) RedirectTo(t *testing.T, expectedLocation string)
- func (a *HTTPAssert) StatusBadRequest(t *testing.T)
- func (a *HTTPAssert) StatusCode(t *testing.T, expected int)
- func (a *HTTPAssert) StatusForbidden(t *testing.T)
- func (a *HTTPAssert) StatusNotFound(t *testing.T)
- func (a *HTTPAssert) StatusOK(t *testing.T)
- func (a *HTTPAssert) StatusRedirect(t *testing.T)
- func (a *HTTPAssert) StatusServerError(t *testing.T)
- func (a *HTTPAssert) StatusUnauthorized(t *testing.T)
- func (a *HTTPAssert) TableRowCount(t *testing.T, expected int)
- type HTTPResponse
- type HTTPSetupOptions
- type HTTPTest
- func (h *HTTPTest) Cleanup()
- func (h *HTTPTest) ClearCookies()
- func (h *HTTPTest) DBPath() string
- func (h *HTTPTest) Delete(path string) *HTTPResponse
- func (h *HTTPTest) ExtractCSRFToken(resp *HTTPResponse) string
- func (h *HTTPTest) FollowRedirect(resp *HTTPResponse) *HTTPResponse
- func (h *HTTPTest) FollowRedirects(resp *HTTPResponse) *HTTPResponse
- func (h *HTTPTest) Get(path string) *HTTPResponse
- func (h *HTTPTest) GetCookie(name string) string
- func (h *HTTPTest) GetWithHeaders(path string, headers map[string]string) *HTTPResponse
- func (h *HTTPTest) OnCleanup(fn func())
- func (h *HTTPTest) PostForm(path string, data url.Values) *HTTPResponse
- func (h *HTTPTest) PostJSON(path string, data interface{}) *HTTPResponse
- func (h *HTTPTest) PostMultipart(path string, fields map[string]string, files map[string][]byte) *HTTPResponse
- func (h *HTTPTest) SetCookie(name, value string)
- func (h *HTTPTest) SetDB(db *sql.DB)
- func (h *HTTPTest) SubmitForm(resp *HTTPResponse, formSelector string, values map[string]string) *HTTPResponse
- func (h *HTTPTest) URL(path string) string
- func (h *HTTPTest) WaitForServer(timeout time.Duration) error
- type ModalTester
- func (m *ModalTester) ClickButton(text string) error
- func (m *ModalTester) ClickSubmit() error
- func (m *ModalTester) Close() error
- func (m *ModalTester) CloseByAction(action string) error
- func (m *ModalTester) FillForm(fields ...Field) error
- func (m *ModalTester) GetText(selector string) (string, error)
- func (m *ModalTester) Open() error
- func (m *ModalTester) OpenByAction(action string) error
- func (m *ModalTester) VerifyHidden() error
- func (m *ModalTester) VerifyText(selector, expectedText string) error
- func (m *ModalTester) VerifyVisible() error
- func (m *ModalTester) WaitForClose(timeout time.Duration) error
- func (m *ModalTester) WaitForOpen(timeout time.Duration) error
- func (m *ModalTester) WithCloseSelector(selector string) *ModalTester
- func (m *ModalTester) WithModalSelector(selector string) *ModalTester
- func (m *ModalTester) WithOpenSelector(selector string) *ModalTester
- type Provider
- type SafeBuffer
- type ServerLogger
- func (sl *ServerLogger) Clear()
- func (sl *ServerLogger) Count() int
- func (sl *ServerLogger) CountMatching(pattern string) int
- func (sl *ServerLogger) FindLog(pattern string) (string, bool)
- func (sl *ServerLogger) FindLogs(pattern string) []string
- func (sl *ServerLogger) GetLastN(n int) []string
- func (sl *ServerLogger) GetLogs() []string
- func (sl *ServerLogger) HasLog(pattern string) bool
- func (sl *ServerLogger) Print()
- func (sl *ServerLogger) PrintLast(n int)
- func (sl *ServerLogger) PrintMatching(pattern string)
- func (sl *ServerLogger) Start()
- func (sl *ServerLogger) Stop()
- func (sl *ServerLogger) Writer() io.Writer
- type SetupOptions
- type SmokeTestOptions
- type SmokeTestResult
- type SmokeTestSuite
- type TestCredentials
- type WSMessage
- type WSMessageLogger
- func (wl *WSMessageLogger) Clear()
- func (wl *WSMessageLogger) Count() int
- func (wl *WSMessageLogger) CountByDirection(direction string) int
- func (wl *WSMessageLogger) CountMatching(pattern string) int
- func (wl *WSMessageLogger) FindMessage(pattern string) (WSMessage, bool)
- func (wl *WSMessageLogger) FindMessages(pattern string) []WSMessage
- func (wl *WSMessageLogger) GetLastN(n int) []WSMessage
- func (wl *WSMessageLogger) GetMessages() []WSMessage
- func (wl *WSMessageLogger) GetMessagesSince(since time.Time) []WSMessage
- func (wl *WSMessageLogger) GetReceived() []WSMessage
- func (wl *WSMessageLogger) GetSent() []WSMessage
- func (wl *WSMessageLogger) HasMessage(pattern string) bool
- func (wl *WSMessageLogger) Print()
- func (wl *WSMessageLogger) PrintLast(n int)
- func (wl *WSMessageLogger) PrintMatching(pattern string)
- func (wl *WSMessageLogger) Start(ctx context.Context)
- func (wl *WSMessageLogger) WaitForMessage(pattern string, timeout time.Duration) (WSMessage, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CleanupAllTestContainers ¶
func CleanupAllTestContainers()
CleanupAllTestContainers removes both Chrome containers and test app containers. This is a convenience function to run all cleanup operations before tests start.
func CleanupChromeContainers ¶
func CleanupChromeContainers()
CleanupChromeContainers removes any lingering Chrome containers created by the test helpers.
func CleanupTestContainers ¶
func CleanupTestContainers()
CleanupTestContainers removes any lingering test app containers (lvt-test-*). These containers are created by runDockerContainer in e2e tests for integration testing. Unlike Chrome containers which use --rm flag, these can linger if tests crash or are interrupted.
func GenerateTestAppName ¶
GenerateTestAppName generates a unique app name for deployment testing
Format: lvt-test-{random}-{timestamp} Example: lvt-test-a3f4b2-1699123456
The name is guaranteed to: - Be unique (random + timestamp) - Be valid for Fly.io (lowercase, alphanumeric, hyphens only) - Be traceable (includes "test" and timestamp) - Be short enough for Fly.io limits (<32 chars)
func GetAWSCredentials ¶
GetAWSCredentials returns AWS credentials for litestream
func GetChromeTestURL ¶
GetChromeTestURL returns the URL for Chrome (in Docker) to access the test server Chrome container uses host.docker.internal to reach the host on all platforms
func GetClientLibraryJS ¶
func GetClientLibraryJS() []byte
GetClientLibraryJS returns the embedded client library for e2e tests
func GetFlyAPIToken ¶
GetFlyAPIToken returns the Fly.io API token from environment
func GetFreePort ¶
GetFreePort asks the kernel for a free open port that is ready to use
func HasCredentials ¶
HasCredentials returns true if credentials are available for the provider (non-testing version)
func IsTestAppName ¶
IsTestAppName checks if a name appears to be a test app
func RequireAWSCredentials ¶
func RequireAWSCredentials(t *stdtesting.T)
RequireAWSCredentials checks if AWS credentials are available (for litestream)
func RequireCredentials ¶
func RequireCredentials(t *stdtesting.T, provider Provider)
RequireCredentials skips the test if credentials for the provider are not available
func RequireFlyCredentials ¶
func RequireFlyCredentials(t *stdtesting.T)
RequireFlyCredentials is a convenience function for Fly.io tests
func ServeClientLibrary ¶
func ServeClientLibrary(w http.ResponseWriter, r *http.Request)
ServeClientLibrary serves the LiveTemplate client browser bundle from embedded bytes. This is for development/testing purposes only. In production, serve from CDN.
func SetupUpdateEventListener ¶
SetupUpdateEventListener sets up a non-blocking event listener that captures 'lvt:updated' events in window.__capturedEvents array. This must be called BEFORE the action that triggers the event. Then use WaitForUpdateEvent to poll for the captured event.
Example:
chromedp.Run(ctx,
SetupUpdateEventListener(), // Setup listener (non-blocking)
chromedp.SendKeys(...), // Trigger action
WaitForUpdateEvent("search", 5*time.Second), // Poll for captured event
)
func StartDockerChrome ¶
StartDockerChrome starts the chromedp headless-shell Docker container Returns an error if the container fails to start or Chrome fails to become ready
func StartTestServer ¶
StartTestServer starts a Go server on the specified port mainPath should be the path to main.go (e.g., "main.go" or "../../examples/counter/main.go")
func StopDockerChrome ¶
StopDockerChrome stops and removes the Chrome Docker container t can be nil for cleanup scenarios (e.g., TestMain cleanup)
func ValidateAppName ¶
ValidateAppName checks if an app name is valid for Fly.io
func ValidateCredentials ¶
ValidateCredentials checks if required credentials are available for a provider
func ValidateNoTemplateExpressions ¶
ValidateNoTemplateExpressions checks that the specified element does not contain raw Go template expressions like {{if}}, {{range}}, {{define}}, etc. This catches the bug where unflattened templates are used in WebSocket tree generation.
func WaitFor ¶
WaitFor polls a JavaScript condition until it returns true or timeout is reached. This is a generic condition-based wait utility that eliminates arbitrary sleeps.
The condition must be a JavaScript expression that evaluates to a boolean.
Examples:
- WaitFor("document.getElementById('modal').style.display === 'flex'", 5*time.Second)
- WaitFor("document.querySelector('.item').textContent === 'Hello'", 3*time.Second)
- WaitFor("document.querySelectorAll('.item').length === 5", 5*time.Second)
- WaitFor("!document.getElementById('modal').hasAttribute('hidden')", 2*time.Second)
func WaitForActionResponse ¶
WaitForActionResponse waits for a WebSocket message with the specified action name in metadata. This is deterministic - it waits for the exact action to complete, not arbitrary time.
The test should clear window.__wsMessages before triggering the action:
chromedp.Evaluate(`window.__wsMessages = [];`, nil)
// ... trigger action ...
WaitForActionResponse("search", 5*time.Second)
Example:
chromedp.Evaluate(`window.__wsMessages = [];`, nil),
chromedp.SendKeys(`input[name="query"]`, "test", chromedp.ByQuery),
WaitForActionResponse("search", 5*time.Second),
func WaitForCount ¶
WaitForCount waits for a specific number of elements to match the selector. This is a convenience wrapper around WaitFor for counting elements.
The selector is a CSS selector, and expectedCount is the exact number of elements expected. Returns an error if the condition is not met within the timeout.
Examples:
WaitForCount("tbody tr", 3, 5*time.Second)
WaitForCount(".todo-item", 10, 10*time.Second)
WaitForCount("button[disabled]", 0, 3*time.Second)
func WaitForMessageCount ¶
WaitForMessageCount waits for the WebSocket message counter to reach the expected value. This is a deterministic way to wait for updates without relying on arbitrary timeouts. The client increments window.__wsMessageCount after each DOM update completes.
Example:
var initialCount int chromedp.Evaluate(`window.__wsMessageCount || 0`, &initialCount) // ... trigger action ... WaitForMessageCount(initialCount+1, 5*time.Second) // Wait for exactly 1 new message
func WaitForText ¶
WaitForText waits for an element's text content to include the specified text. This is a convenience wrapper around WaitFor for common text-matching scenarios.
The selector is a CSS selector, and text is the substring to match. Returns an error if the condition is not met within the timeout.
Examples:
WaitForText("section", "No results found", 5*time.Second)
WaitForText("tbody", "First Todo Item", 10*time.Second)
WaitForText(".status", "Connected", 3*time.Second)
func WaitForUpdateEvent ¶
WaitForUpdateEvent polls for a captured 'lvt:updated' event. Must be used after SetupUpdateEventListener(). Optionally filters by action name if provided.
This is deterministic - it waits for the actual event to fire, not arbitrary timeouts.
func WaitForWebSocketReady ¶
WaitForWebSocketReady waits for the first WebSocket update to be applied by polling for the removal of data-lvt-loading attribute (condition-based waiting). This ensures E2E tests run after the WebSocket connection is established and initial state is synchronized.
Types ¶
type Assert ¶
type Assert struct {
// contains filtered or unexported fields
}
Assert provides assertion helpers for e2e tests.
func NewAssert ¶
NewAssert creates an assertion helper for the given test.
Example:
assert := lvttest.NewAssert(test)
assert.PageContains("Welcome")
assert.WebSocketConnected()
func (*Assert) AttributeValue ¶
AttributeValue verifies that an element has the expected attribute value.
func (*Assert) ElementCount ¶
ElementCount verifies that exactly expectedCount elements match the selector.
func (*Assert) ElementExists ¶
ElementExists verifies that at least one element matches the selector.
func (*Assert) ElementHidden ¶
ElementHidden verifies that an element matching the selector is hidden or doesn't exist.
func (*Assert) ElementNotExists ¶
ElementNotExists verifies that no elements match the selector.
func (*Assert) ElementVisible ¶
ElementVisible verifies that an element matching the selector is visible.
func (*Assert) FormFieldValue ¶
FormFieldValue verifies that a form field has the expected value.
func (*Assert) NoConsoleErrors ¶
NoConsoleErrors verifies that there are no console errors. This is useful for catching JavaScript errors during tests.
func (*Assert) NoTemplateErrors ¶
NoTemplateErrors verifies that the page does not contain raw Go template expressions. This catches bugs where unflattened templates are sent to the client.
func (*Assert) NotHasClass ¶
NotHasClass verifies that an element does NOT have the expected CSS class.
func (*Assert) PageContains ¶
PageContains verifies that the page body contains the given text.
func (*Assert) PageNotContains ¶
PageNotContains verifies that the page body does NOT contain the given text.
func (*Assert) TableRowCount ¶
TableRowCount verifies that a table has the expected number of rows. This counts tbody tr elements by default.
func (*Assert) TextContains ¶
TextContains verifies that an element's text contains the expected substring.
func (*Assert) TextContent ¶
TextContent verifies that an element has the expected text content.
func (*Assert) WebSocketConnected ¶
WebSocketConnected verifies that the WebSocket connection is established. This checks if the data-lvt-loading attribute has been removed from the wrapper.
type CRUDTester ¶
type CRUDTester struct {
// contains filtered or unexported fields
}
CRUDTester provides helpers for testing CRUD operations on resources.
func NewCRUDTester ¶
func NewCRUDTester(test *E2ETest, resourcePath string) *CRUDTester
NewCRUDTester creates a CRUD tester for the given resource path.
Example:
crud := lvttest.NewCRUDTester(test, "/products")
crud.Create(
lvttest.TextField("name", "MacBook Pro"),
lvttest.FloatField("price", 2499.99),
)
func (*CRUDTester) Create ¶
func (c *CRUDTester) Create(fields ...Field) error
Create fills the create form with the given fields and submits it. It waits for the WebSocket update to complete after submission.
func (*CRUDTester) Delete ¶
func (c *CRUDTester) Delete(recordID string) error
Delete clicks the delete button for a record and confirms. The recordID is used to identify which record to delete (e.g., using lvt-data-id).
func (*CRUDTester) Edit ¶
func (c *CRUDTester) Edit(recordID string, fields ...Field) error
Edit opens the edit form for a record, fills the fields, and saves. The recordID is used to identify which record to edit (e.g., using lvt-data-id).
func (*CRUDTester) GetTableRows ¶
func (c *CRUDTester) GetTableRows() ([]map[string]string, error)
GetTableRows extracts table row data from the page. This is useful for verifying record order, content, etc.
func (*CRUDTester) VerifyExists ¶
func (c *CRUDTester) VerifyExists(searchText string) error
VerifyExists checks if a record containing the given text appears in the page.
func (*CRUDTester) VerifyNotExists ¶
func (c *CRUDTester) VerifyNotExists(searchText string) error
VerifyNotExists checks if a record containing the given text does NOT appear in the page.
type ChromeMode ¶
type ChromeMode string
ChromeMode specifies how Chrome should be launched.
const ( // ChromeDocker uses chromedp/headless-shell Docker container (default) ChromeDocker ChromeMode = "docker" // ChromeLocal uses locally installed Chrome/Chromium ChromeLocal ChromeMode = "local" ChromeShared ChromeMode = "shared" )
type ConsoleLog ¶
type ConsoleLogger ¶
type ConsoleLogger struct {
// contains filtered or unexported fields
}
func NewConsoleLogger ¶
func NewConsoleLogger() *ConsoleLogger
func (*ConsoleLogger) Clear ¶
func (cl *ConsoleLogger) Clear()
func (*ConsoleLogger) Count ¶
func (cl *ConsoleLogger) Count() int
func (*ConsoleLogger) CountByType ¶
func (cl *ConsoleLogger) CountByType(logType string) int
func (*ConsoleLogger) FilterByType ¶
func (cl *ConsoleLogger) FilterByType(logType string) []ConsoleLog
func (*ConsoleLogger) FindLog ¶
func (cl *ConsoleLogger) FindLog(pattern string) (ConsoleLog, bool)
func (*ConsoleLogger) GetErrors ¶
func (cl *ConsoleLogger) GetErrors() []ConsoleLog
func (*ConsoleLogger) GetLogs ¶
func (cl *ConsoleLogger) GetLogs() []ConsoleLog
func (*ConsoleLogger) GetWarnings ¶
func (cl *ConsoleLogger) GetWarnings() []ConsoleLog
func (*ConsoleLogger) HasErrors ¶
func (cl *ConsoleLogger) HasErrors() bool
func (*ConsoleLogger) HasWarnings ¶
func (cl *ConsoleLogger) HasWarnings() bool
func (*ConsoleLogger) Print ¶
func (cl *ConsoleLogger) Print()
func (*ConsoleLogger) PrintErrors ¶
func (cl *ConsoleLogger) PrintErrors()
func (*ConsoleLogger) Start ¶
func (cl *ConsoleLogger) Start(ctx context.Context)
type DatabaseAssert ¶
type DatabaseAssert struct {
// contains filtered or unexported fields
}
DatabaseAssert provides database assertion helpers.
func NewDatabaseAssert ¶
func NewDatabaseAssert(test *HTTPTest) *DatabaseAssert
NewDatabaseAssert creates a database assertion helper.
func (*DatabaseAssert) RecordCount ¶
func (d *DatabaseAssert) RecordCount(t *testing.T, table string, expected int)
RecordCount asserts that the table has exactly the expected number of records.
func (*DatabaseAssert) RecordDeleted ¶
func (d *DatabaseAssert) RecordDeleted(t *testing.T, table string, id interface{})
RecordDeleted asserts that a record with the given ID was deleted.
func (*DatabaseAssert) RecordExists ¶
func (d *DatabaseAssert) RecordExists(t *testing.T, table string, conditions map[string]interface{})
RecordExists asserts that a record exists in the table matching the conditions.
func (*DatabaseAssert) RecordNotExists ¶
func (d *DatabaseAssert) RecordNotExists(t *testing.T, table string, conditions map[string]interface{})
RecordNotExists asserts that no record exists in the table matching the conditions.
type DeploymentOptions ¶
type DeploymentOptions struct {
Provider Provider
AppName string // If empty, generates unique name
AppDir string // If empty, creates test app
Region string // Cloud region (defaults vary by provider)
Kit string // App kit: multi, single, simple (default: multi)
// Feature flags
WithAuth bool
WithLitestream bool
WithS3Backup bool
// Resource options
Resources []string // Resources to add (e.g., "posts title content")
}
DeploymentOptions configures deployment test setup
type DeploymentTest ¶
type DeploymentTest struct {
T *stdtesting.T
// App metadata
Provider Provider
AppName string
AppDir string
AppURL string
Region string
// Provider-specific clients (for debugging and inspection)
DockerClient *providers.DockerClient
// contains filtered or unexported fields
}
DeploymentTest manages the lifecycle of a deployed test application
func SetupDeployment ¶
func SetupDeployment(t *stdtesting.T, opts *DeploymentOptions) *DeploymentTest
SetupDeployment creates a test app and prepares it for deployment
func (*DeploymentTest) AddCleanup ¶
func (dt *DeploymentTest) AddCleanup(fn func() error)
AddCleanup registers a cleanup function to be called when test ends
func (*DeploymentTest) Cleanup ¶
func (dt *DeploymentTest) Cleanup() error
Cleanup destroys all resources created during the test
func (*DeploymentTest) Deploy ¶
func (dt *DeploymentTest) Deploy() error
Deploy executes the deployment to the configured provider
func (*DeploymentTest) VerifyHealth ¶
func (dt *DeploymentTest) VerifyHealth() error
VerifyHealth checks if the deployed app is responding
func (*DeploymentTest) VerifyWebSocket ¶
func (dt *DeploymentTest) VerifyWebSocket() error
VerifyWebSocket checks if WebSocket connection can be established
type E2ETest ¶
type E2ETest struct {
T *testing.T
Context context.Context
Cancel context.CancelFunc
ServerPort int
ChromePort int
ChromeMode ChromeMode
ServerCmd *exec.Cmd
AppDir string
AppPath string
// Loggers for debugging
Console *ConsoleLogger
Server *ServerLogger
WebSocket *WSMessageLogger
// contains filtered or unexported fields
}
E2ETest represents a configured e2e test environment with Chrome, server, and test context.
func Setup ¶
func Setup(t *testing.T, opts *SetupOptions) *E2ETest
Setup creates a complete e2e test environment with Chrome, server, and test context. It automatically:
- Starts Chrome (Docker by default)
- Starts the test server
- Creates chromedp context
- Sets up console log capture (if enabled)
Example:
test := lvttest.Setup(t, &lvttest.SetupOptions{
AppPath: "./main.go",
})
defer test.Cleanup()
test.Navigate("/")
func (*E2ETest) Cleanup ¶
func (e *E2ETest) Cleanup()
Cleanup tears down all test resources (Chrome, server, contexts). This should be called with defer after Setup().
type Field ¶
Field represents a form field to be filled during testing.
func FloatField ¶
FloatField creates a field for float input.
func SelectField ¶
SelectField creates a field for select dropdown.
func TextAreaField ¶
TextAreaField creates a field for textarea input.
type HTTPAssert ¶
type HTTPAssert struct {
Response *HTTPResponse
}
HTTPAssert provides assertion helpers for HTTP responses.
func NewHTTPAssert ¶
func NewHTTPAssert(resp *HTTPResponse) *HTTPAssert
NewHTTPAssert creates an assertion helper for the given HTTP response.
Example:
resp := test.Get("/")
assert := lvttest.NewHTTPAssert(resp)
assert.StatusOK(t)
assert.Contains(t, "Welcome")
func (*HTTPAssert) Contains ¶
func (a *HTTPAssert) Contains(t *testing.T, text string)
Contains asserts that the response body contains the expected text.
func (*HTTPAssert) ContainsAll ¶
func (a *HTTPAssert) ContainsAll(t *testing.T, texts ...string)
ContainsAll asserts that the response body contains all expected texts.
func (*HTTPAssert) ContentType ¶
func (a *HTTPAssert) ContentType(t *testing.T, expected string)
ContentType asserts that the response has the expected Content-Type.
func (*HTTPAssert) ContentTypeHTML ¶
func (a *HTTPAssert) ContentTypeHTML(t *testing.T)
ContentTypeHTML asserts that the response is HTML.
func (*HTTPAssert) ContentTypeJSON ¶
func (a *HTTPAssert) ContentTypeJSON(t *testing.T)
ContentTypeJSON asserts that the response is JSON.
func (*HTTPAssert) ElementCount ¶
func (a *HTTPAssert) ElementCount(t *testing.T, selector string, expected int)
ElementCount asserts that exactly n elements match the selector.
func (*HTTPAssert) ElementText ¶
func (a *HTTPAssert) ElementText(t *testing.T, selector string, expectedText string)
ElementText asserts that an element has the expected text content.
func (*HTTPAssert) ElementTextContains ¶
func (a *HTTPAssert) ElementTextContains(t *testing.T, selector string, expectedSubstring string)
ElementTextContains asserts that an element's text contains the expected substring.
func (*HTTPAssert) FormFieldValue ¶
func (a *HTTPAssert) FormFieldValue(t *testing.T, fieldName string, expectedValue string)
FormFieldValue asserts that a form field has the expected value.
func (*HTTPAssert) HasCSRFToken ¶
func (a *HTTPAssert) HasCSRFToken(t *testing.T)
HasCSRFToken asserts that the response contains a CSRF token field.
func (*HTTPAssert) HasElement ¶
func (a *HTTPAssert) HasElement(t *testing.T, selector string)
HasElement asserts that at least one element matches the CSS selector. Note: This uses a simple implementation - for complex selectors, use a full CSS selector library.
func (*HTTPAssert) HasFormField ¶
func (a *HTTPAssert) HasFormField(t *testing.T, fieldName string)
HasFormField asserts that a form field with the given name exists.
func (*HTTPAssert) HasHeader ¶
func (a *HTTPAssert) HasHeader(t *testing.T, name string)
HasHeader asserts that the response has the specified header.
func (*HTTPAssert) HasNoElement ¶
func (a *HTTPAssert) HasNoElement(t *testing.T, selector string)
HasNoElement asserts that no elements match the CSS selector.
func (*HTTPAssert) Header ¶
func (a *HTTPAssert) Header(t *testing.T, name, expected string)
Header asserts that the response has the expected header value.
func (*HTTPAssert) Matches ¶
func (a *HTTPAssert) Matches(t *testing.T, pattern string)
Matches asserts that the response body matches the regular expression.
func (*HTTPAssert) NoTemplateErrors ¶
func (a *HTTPAssert) NoTemplateErrors(t *testing.T)
NoTemplateErrors asserts that the response has no unflattened template expressions. This catches bugs where {{.Field}}, {{if}}, {{range}}, etc. appear in the output.
func (*HTTPAssert) NotContains ¶
func (a *HTTPAssert) NotContains(t *testing.T, text string)
NotContains asserts that the response body does NOT contain the text.
func (*HTTPAssert) RedirectTo ¶
func (a *HTTPAssert) RedirectTo(t *testing.T, expectedLocation string)
RedirectTo asserts that the response redirects to the expected location.
func (*HTTPAssert) StatusBadRequest ¶
func (a *HTTPAssert) StatusBadRequest(t *testing.T)
StatusBadRequest asserts that the response has a 400 status.
func (*HTTPAssert) StatusCode ¶
func (a *HTTPAssert) StatusCode(t *testing.T, expected int)
StatusCode asserts that the response has the expected status code.
func (*HTTPAssert) StatusForbidden ¶
func (a *HTTPAssert) StatusForbidden(t *testing.T)
StatusForbidden asserts that the response has a 403 status.
func (*HTTPAssert) StatusNotFound ¶
func (a *HTTPAssert) StatusNotFound(t *testing.T)
StatusNotFound asserts that the response has a 404 status.
func (*HTTPAssert) StatusOK ¶
func (a *HTTPAssert) StatusOK(t *testing.T)
StatusOK asserts that the response has a 200 OK status.
func (*HTTPAssert) StatusRedirect ¶
func (a *HTTPAssert) StatusRedirect(t *testing.T)
StatusRedirect asserts that the response is a redirect (3xx).
func (*HTTPAssert) StatusServerError ¶
func (a *HTTPAssert) StatusServerError(t *testing.T)
StatusServerError asserts that the response has a 5xx status.
func (*HTTPAssert) StatusUnauthorized ¶
func (a *HTTPAssert) StatusUnauthorized(t *testing.T)
StatusUnauthorized asserts that the response has a 401 status.
func (*HTTPAssert) TableRowCount ¶
func (a *HTTPAssert) TableRowCount(t *testing.T, expected int)
TableRowCount asserts that a table has the expected number of rows.
type HTTPResponse ¶
HTTPResponse wraps an HTTP response with helper methods.
func (*HTTPResponse) FindTemplateErrors ¶
func (r *HTTPResponse) FindTemplateErrors() []string
FindTemplateErrors returns all template error matches found in the response.
func (*HTTPResponse) HasTemplateErrors ¶
func (r *HTTPResponse) HasTemplateErrors() bool
HasTemplateErrors checks if the response body contains unflattened template expressions.
func (*HTTPResponse) String ¶
func (r *HTTPResponse) String() string
String returns the response body as a string.
type HTTPSetupOptions ¶
type HTTPSetupOptions struct {
// AppPath is the path to main.go (e.g., "./cmd/myapp/main.go")
AppPath string
// AppDir is the working directory for the app (defaults to directory of AppPath)
AppDir string
// Port is the server port (auto-allocated if 0)
Port int
// Timeout is the HTTP client timeout (default 10s)
Timeout time.Duration
// DB is an optional database connection for state verification
DB *sql.DB
}
HTTPSetupOptions configures the HTTP test environment.
type HTTPTest ¶
type HTTPTest struct {
T *testing.T
Port int
Client *http.Client
DB *sql.DB
AppDir string
AppPath string
BaseURL string
ServerCmd *exec.Cmd
Server *ServerLogger
// contains filtered or unexported fields
}
HTTPTest represents a configured HTTP test environment. It starts the test server and provides HTTP client methods for testing.
func SetupHTTP ¶
func SetupHTTP(t *testing.T, opts *HTTPSetupOptions) *HTTPTest
SetupHTTP creates a new HTTP test environment. It starts the test server and returns an HTTPTest for making requests.
Example:
test := lvttest.SetupHTTP(t, &lvttest.HTTPSetupOptions{
AppPath: "../../cmd/myapp/main.go",
})
defer test.Cleanup()
resp := test.Get("/")
assert := lvttest.NewHTTPAssert(resp)
assert.StatusOK(t)
func (*HTTPTest) Cleanup ¶
func (h *HTTPTest) Cleanup()
Cleanup tears down the test environment. This is called automatically via t.Cleanup(), but can be called manually if needed.
func (*HTTPTest) DBPath ¶
DBPath returns the path to the test database. This assumes TEST_MODE=1 is set, which uses :memory: by default.
func (*HTTPTest) Delete ¶
func (h *HTTPTest) Delete(path string) *HTTPResponse
Delete performs an HTTP DELETE request and returns the response.
func (*HTTPTest) ExtractCSRFToken ¶
func (h *HTTPTest) ExtractCSRFToken(resp *HTTPResponse) string
ExtractCSRFToken extracts a CSRF token from an HTML response. It looks for a hidden input field named "csrf_token" or "_csrf".
func (*HTTPTest) FollowRedirect ¶
func (h *HTTPTest) FollowRedirect(resp *HTTPResponse) *HTTPResponse
FollowRedirect follows a redirect response and returns the new response. Returns nil if the response is not a redirect.
func (*HTTPTest) FollowRedirects ¶
func (h *HTTPTest) FollowRedirects(resp *HTTPResponse) *HTTPResponse
FollowRedirects follows all redirects until a non-redirect response.
func (*HTTPTest) Get ¶
func (h *HTTPTest) Get(path string) *HTTPResponse
Get performs an HTTP GET request and returns the response.
func (*HTTPTest) GetWithHeaders ¶
func (h *HTTPTest) GetWithHeaders(path string, headers map[string]string) *HTTPResponse
GetWithHeaders performs an HTTP GET request with custom headers.
func (*HTTPTest) OnCleanup ¶
func (h *HTTPTest) OnCleanup(fn func())
OnCleanup registers a function to be called during cleanup.
func (*HTTPTest) PostForm ¶
func (h *HTTPTest) PostForm(path string, data url.Values) *HTTPResponse
PostForm submits form data via POST and returns the response.
func (*HTTPTest) PostJSON ¶
func (h *HTTPTest) PostJSON(path string, data interface{}) *HTTPResponse
PostJSON submits JSON data via POST and returns the response.
func (*HTTPTest) PostMultipart ¶
func (h *HTTPTest) PostMultipart(path string, fields map[string]string, files map[string][]byte) *HTTPResponse
PostMultipart submits multipart form data (for file uploads) and returns the response.
func (*HTTPTest) SubmitForm ¶
func (h *HTTPTest) SubmitForm(resp *HTTPResponse, formSelector string, values map[string]string) *HTTPResponse
SubmitForm extracts a form from the response, fills in values, and submits it. It automatically handles CSRF tokens.
type ModalTester ¶
type ModalTester struct {
// contains filtered or unexported fields
}
ModalTester provides methods for testing modal dialogs.
func NewModalTester ¶
func NewModalTester(test *E2ETest) *ModalTester
NewModalTester creates a modal tester with default selectors. Default modal selector: "[data-test-id='modal']" or ".modal" You can customize selectors using WithSelectors().
func (*ModalTester) ClickButton ¶
func (m *ModalTester) ClickButton(text string) error
ClickButton clicks a button inside the modal. This searches for a button containing the specified text within the modal.
func (*ModalTester) ClickSubmit ¶
func (m *ModalTester) ClickSubmit() error
ClickSubmit clicks the submit button inside the modal and waits for WebSocket update. This is a convenience method for form submission.
func (*ModalTester) Close ¶
func (m *ModalTester) Close() error
Close closes the modal by clicking the close selector. If no close selector is set, this will fail.
func (*ModalTester) CloseByAction ¶
func (m *ModalTester) CloseByAction(action string) error
CloseByAction closes the modal using a LiveTemplate action.
func (*ModalTester) FillForm ¶
func (m *ModalTester) FillForm(fields ...Field) error
FillForm fills a form inside the modal using Field definitions. This is useful for modals that contain forms for create/edit operations.
func (*ModalTester) GetText ¶
func (m *ModalTester) GetText(selector string) (string, error)
GetText gets the text content of an element inside the modal.
func (*ModalTester) Open ¶
func (m *ModalTester) Open() error
Open opens the modal by clicking the open selector. If no open selector is set, this will fail.
func (*ModalTester) OpenByAction ¶
func (m *ModalTester) OpenByAction(action string) error
OpenByAction opens the modal using a LiveTemplate action. This clicks the element with lvt-on-click attribute matching the action.
func (*ModalTester) VerifyHidden ¶
func (m *ModalTester) VerifyHidden() error
VerifyHidden verifies that the modal is hidden.
func (*ModalTester) VerifyText ¶
func (m *ModalTester) VerifyText(selector, expectedText string) error
VerifyText verifies that an element inside the modal contains expected text.
func (*ModalTester) VerifyVisible ¶
func (m *ModalTester) VerifyVisible() error
VerifyVisible verifies that the modal is visible. This checks if the modal element exists and has a visible class or style.
func (*ModalTester) WaitForClose ¶
func (m *ModalTester) WaitForClose(timeout time.Duration) error
WaitForClose waits for the modal to close with a timeout.
func (*ModalTester) WaitForOpen ¶
func (m *ModalTester) WaitForOpen(timeout time.Duration) error
WaitForOpen waits for the modal to open with a timeout.
func (*ModalTester) WithCloseSelector ¶
func (m *ModalTester) WithCloseSelector(selector string) *ModalTester
WithCloseSelector sets the selector for the button/element that closes the modal.
func (*ModalTester) WithModalSelector ¶
func (m *ModalTester) WithModalSelector(selector string) *ModalTester
WithModalSelector sets a custom modal selector.
func (*ModalTester) WithOpenSelector ¶
func (m *ModalTester) WithOpenSelector(selector string) *ModalTester
WithOpenSelector sets the selector for the button/element that opens the modal.
type SafeBuffer ¶
type SafeBuffer struct {
// contains filtered or unexported fields
}
SafeBuffer is a concurrency-safe buffer for capturing logs from processes or goroutines.
func (*SafeBuffer) Bytes ¶
func (b *SafeBuffer) Bytes() []byte
Bytes returns a copy of the buffered bytes.
func (*SafeBuffer) String ¶
func (b *SafeBuffer) String() string
String returns the buffered contents as a string.
type ServerLogger ¶
type ServerLogger struct {
// contains filtered or unexported fields
}
func NewServerLogger ¶
func NewServerLogger() *ServerLogger
func (*ServerLogger) Clear ¶
func (sl *ServerLogger) Clear()
func (*ServerLogger) Count ¶
func (sl *ServerLogger) Count() int
func (*ServerLogger) CountMatching ¶
func (sl *ServerLogger) CountMatching(pattern string) int
func (*ServerLogger) FindLogs ¶
func (sl *ServerLogger) FindLogs(pattern string) []string
func (*ServerLogger) GetLastN ¶
func (sl *ServerLogger) GetLastN(n int) []string
func (*ServerLogger) GetLogs ¶
func (sl *ServerLogger) GetLogs() []string
func (*ServerLogger) HasLog ¶
func (sl *ServerLogger) HasLog(pattern string) bool
func (*ServerLogger) Print ¶
func (sl *ServerLogger) Print()
func (*ServerLogger) PrintLast ¶
func (sl *ServerLogger) PrintLast(n int)
func (*ServerLogger) PrintMatching ¶
func (sl *ServerLogger) PrintMatching(pattern string)
func (*ServerLogger) Start ¶
func (sl *ServerLogger) Start()
func (*ServerLogger) Stop ¶
func (sl *ServerLogger) Stop()
func (*ServerLogger) Writer ¶
func (sl *ServerLogger) Writer() io.Writer
type SetupOptions ¶
type SetupOptions struct {
AppPath string // Path to main.go (e.g., "./main.go")
Port int // Server port (auto-allocated if 0)
Timeout time.Duration // Test timeout (default 60s)
CaptureConsole bool // Capture browser console (default true)
ChromeMode ChromeMode // Chrome mode (default: ChromeDocker)
ChromePath string // Path to local Chrome binary (for ChromeLocal mode)
}
SetupOptions configures the test environment.
type SmokeTestOptions ¶
type SmokeTestOptions struct {
Timeout time.Duration // Total timeout for all tests
RetryDelay time.Duration // Delay between retries
MaxRetries int // Maximum retry attempts
SkipBrowser bool // Skip browser-based tests
}
SmokeTestOptions configures smoke test behavior
func DefaultSmokeTestOptions ¶
func DefaultSmokeTestOptions() *SmokeTestOptions
DefaultSmokeTestOptions returns sensible defaults
type SmokeTestResult ¶
SmokeTestResult represents the result of a single smoke test
type SmokeTestSuite ¶
type SmokeTestSuite struct {
AppURL string
Results []SmokeTestResult
TotalDuration time.Duration
}
SmokeTestSuite represents the results of all smoke tests
func RunSmokeTests ¶
func RunSmokeTests(appURL string, opts *SmokeTestOptions) (*SmokeTestSuite, error)
RunSmokeTests executes all smoke tests against a deployed app
func (*SmokeTestSuite) AllPassed ¶
func (s *SmokeTestSuite) AllPassed() bool
AllPassed returns true if all smoke tests passed
func (*SmokeTestSuite) PrintResults ¶
func (s *SmokeTestSuite) PrintResults()
PrintResults prints smoke test results in a readable format
type TestCredentials ¶
type TestCredentials struct {
// Fly.io credentials
FlyAPIToken string
// AWS credentials (for litestream S3 backups)
AWSAccessKeyID string
AWSSecretAccessKey string
S3Bucket string
S3Region string
// DigitalOcean credentials
DOAPIToken string
// Kubernetes credentials (optional, uses local kubeconfig by default)
KubeConfig string
}
TestCredentials holds all credentials needed for deployment testing
func LoadTestCredentials ¶
func LoadTestCredentials() (*TestCredentials, error)
LoadTestCredentials loads credentials from environment variables
type WSMessageLogger ¶
type WSMessageLogger struct {
// contains filtered or unexported fields
}
func NewWSMessageLogger ¶
func NewWSMessageLogger() *WSMessageLogger
func (*WSMessageLogger) Clear ¶
func (wl *WSMessageLogger) Clear()
func (*WSMessageLogger) Count ¶
func (wl *WSMessageLogger) Count() int
func (*WSMessageLogger) CountByDirection ¶
func (wl *WSMessageLogger) CountByDirection(direction string) int
func (*WSMessageLogger) CountMatching ¶
func (wl *WSMessageLogger) CountMatching(pattern string) int
func (*WSMessageLogger) FindMessage ¶
func (wl *WSMessageLogger) FindMessage(pattern string) (WSMessage, bool)
func (*WSMessageLogger) FindMessages ¶
func (wl *WSMessageLogger) FindMessages(pattern string) []WSMessage
func (*WSMessageLogger) GetLastN ¶
func (wl *WSMessageLogger) GetLastN(n int) []WSMessage
func (*WSMessageLogger) GetMessages ¶
func (wl *WSMessageLogger) GetMessages() []WSMessage
func (*WSMessageLogger) GetMessagesSince ¶
func (wl *WSMessageLogger) GetMessagesSince(since time.Time) []WSMessage
func (*WSMessageLogger) GetReceived ¶
func (wl *WSMessageLogger) GetReceived() []WSMessage
func (*WSMessageLogger) GetSent ¶
func (wl *WSMessageLogger) GetSent() []WSMessage
func (*WSMessageLogger) HasMessage ¶
func (wl *WSMessageLogger) HasMessage(pattern string) bool
func (*WSMessageLogger) Print ¶
func (wl *WSMessageLogger) Print()
func (*WSMessageLogger) PrintLast ¶
func (wl *WSMessageLogger) PrintLast(n int)
func (*WSMessageLogger) PrintMatching ¶
func (wl *WSMessageLogger) PrintMatching(pattern string)
func (*WSMessageLogger) Start ¶
func (wl *WSMessageLogger) Start(ctx context.Context)