Skip to content

Swift version of the Playwright testing and automation library.

License

Notifications You must be signed in to change notification settings

vinayjn/playwright-swift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

87 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PlaywrightSwift

A Swift library for browser automation using Playwright. Control Chromium, Firefox, and WebKit browsers with a modern, async/await API.

Requirements

  • macOS 14.0+
  • Swift 6.0+ (uses Swift Macros)

Installation

Add PlaywrightSwift to your Package.swift:

dependencies: [
    .package(url: "https://github.com/vinayjn/playwright-swift.git", from: "1.0.0")
]

Quick Start

Install Browsers

Before running automation, install the browser binaries:

import PlaywrightSwift

try await Playwright.installBrowsers()

Basic Usage

Given this HTML:

<form id="search-form">
  <input type="text" placeholder="Search..." />
  <button type="submit">Search</button>
</form>

Automate it with:

import PlaywrightSwift

// Launch browser
let playwright = try await Playwright.create()
let browser = try await playwright.chromium.launch()

// Create page and navigate
let page = try await browser.newPage()
try await page.goto("https://example.com")

// Interact with elements
try await page.locator("input").fill("Playwright")
try await page.locator("button").click()

// Take a screenshot
try await page.screenshot(options: .init(path: "screenshot.png"))

try await browser.close()

TypedLocators (Type-Safe Element Access)

Given this HTML:

<form id="login">
  <input id="email" type="email" name="email" />
  <input id="password" type="password" name="password" />
  <button id="submit" data-testid="login-btn">Log In</button>
</form>
<nav>
  <a class="nav" href="/dashboard">Dashboard</a>
</nav>

Use TypedLocators for compile-time type safety:

// Create typed locators with element-specific methods
let emailInput = page.input("#email")     // TypedLocator<InputElement>
let submitBtn = page.button("#submit")    // TypedLocator<ButtonElement>
let navLink = page.link("a.nav")          // TypedLocator<LinkElement>

// Type-specific methods only available on correct types
try await emailInput.fill("user@example.com")  // ✅ Works
try await emailInput.clear()                    // ✅ Works
// submitBtn.fill("text")                       // ❌ Won't compile

// Type-safe attribute access
let href = try await navLink.getHref()         // Returns "/dashboard"
let testId = try await submitBtn.getAttribute("data-testid")

Documentation

Learn more about specific features:

Key Features

  • TypedLocators: Phantom types provide compile-time enforcement of element-specific APIs.
  • Swift Macros: Compile-time selector validation with #selector and type-safe Page Objects with @PageObject.
  • Auto-waiting: Locators automatically wait for elements to be ready before acting.
  • Type-safe API: Leverage Swift's type system for browser options and results.
  • Modern Concurrency: Built from the ground up using Swift's async/await.
  • Event Streams: Type-safe AsyncSequence-based event handling for console, requests, responses, dialogs, and downloads.
  • Cross-browser: Support for Chromium, Firefox, and WebKit.
  • Network Mocking: Intercept any network request to mock backend responses.
  • Video & Downloads: Record test execution and manage file downloads.

Type-Safe Selectors

Given this HTML:

<button role="button" name="Submit">Submit Order</button>
<input data-testid="email-input" type="email" />
<a href="/help">Click here for help</a>

Use the Selector enum for type-safe, autocomplete-friendly selectors:

// Role-based selector (matches the <button>)
let button = page.locator(.role(.button, name: "Submit"))

// Test ID selector (matches the <input>)
let input = page.locator(.testId("email-input"))

// Text-based selector (matches the <a>)
let link = page.locator(.text("Click here", exact: false))

Result Builders

Build complex selectors with the locator chain builder:

Given this HTML:

<div data-testid="product-list">
  <li>Samsung Galaxy</li>
  <li>iPhone 15 Pro</li>
  <li>Google Pixel</li>
</div>
// Find the iPhone item within the product list
let item = page.locate {
    LocatorStep.testId("product-list")
    LocatorStep.css("li")
    LocatorStep.filter(hasText: "iPhone")
}

Configure network routes declaratively:

try await page.configureRoutes {
    RouteConfig.mock("**/api/user", with: "{\"name\": \"Test\"}")
    RouteConfig.block("**/*.analytics.js")
    RouteConfig.passthrough("**/*")
}

Wait Conditions

Given this HTML that updates dynamically:

<div id="status">Loading...</div>
<!-- After API call completes, changes to: -->
<div id="status">Success</div>

Wait for specific element states:

let statusDiv = page.locator("#status")

try await statusDiv.waitFor(.visible)
try await statusDiv.waitFor(.hasText("Success"))
try await statusDiv.waitFor(.enabled, timeout: .seconds(10))

Logging & Debugging

Enable detailed logging and protocol tracing for debugging:

// Enable detailed logs
PlaywrightLogger.level = .debug

// Enable verbose protocol tracing
ProtocolTracer.shared.isEnabled = true
ProtocolTracer.shared.methodFilter = "goto" // Optional filter

Testing Support

Use PageTestCase for XCTest integration:

import PlaywrightSwift
import XCTest

class MyAppTests: PageTestCase {
    func testLogin() async throws {
        try await page.goto("https://app.example.com")
        // ...
    }
}

License

MIT License - see LICENSE for details.

About

Swift version of the Playwright testing and automation library.

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors

Languages