Quick summary: This chapter provides complete setup guides for Python + pytest + Playwright and TypeScript + Playwright test automation. Python has simpler syntax and is faster to start (ideal for QA teams), while TypeScript offers better IDE support and type safety (preferred by JavaScript teams). Both include CI/CD configuration with GitHub Actions, parallel execution, cross-browser testing, and debugging tools like trace viewer and HTML reports.
Introduction
You've learned the concepts. You understand what tests are, why automation matters, and which tools exist. Now let's build something real.
This chapter covers complete test automation with Python and JavaScript, giving you hands-on implementation guides for Playwright. I'm going to show you how to set up test automation from scratch. Not theory, actual working projects you can run today. We'll build the same login test in both Python and TypeScript. You'll see the differences, the trade-offs, and which one fits your team.
By the end, you'll have complete projects running locally and in CI/CD.
You can follow this guide with code examples with this GitHub repository.
Choosing Your Stack: Python vs TypeScript
You need to pick a language. Both work brilliantly with Playwright. (If you haven't settled on Playwright yet, Chapter 4 compares it against the alternatives.) The choice comes down to your team.
Python has simpler syntax. You write less code to achieve the same result. If your QA team isn't deeply technical or comes from a manual testing background, Python removes friction. The learning curve is gentler.
TypeScript offers type safety. Your IDE catches errors before you run tests. If your team already writes JavaScript for the product, TypeScript keeps everyone in the same ecosystem. No context switching.
We'll build both. You decide which fits.
If choosing between Python and JavaScript for your test automation feels like the wrong question, Autonoma removes the language decision entirely. AI agents read your codebase, decide what needs testing, and generate and run the E2E tests, no scripts required in any language.
Python Test Automation Setup
Environment Setup
First, create a virtual environment. This isolates your dependencies from other Python projects.
# Create project directory
mkdir test-automation-python
cd test-automation-python
# Create virtual environment
python -m venv venv
# Activate it (Mac/Linux)
source venv/bin/activate
# Activate it (Windows)
venv\Scripts\activate
# Install dependencies
pip install pytest playwright pytest-playwright
# Install browser binaries
playwright installYour virtual environment keeps projects separate. When you activate it, pip install only affects this project.
Project Structure
Create this structure:
The Python project keeps its config in pytest.ini and requirements.txt, while the TypeScript project uses playwright.config.ts and package.json. Both mirror the same tests and CI workflow folders.
test-automation-python/
├── tests/
│ ├── __init__.py
│ └── test_login.py
├── pytest.ini
├── requirements.txt
└── .github/
└── workflows/
└── tests.yml
The tests/ directory holds your test files. pytest discovers anything starting with test_.
Configuration Files
Create pytest.ini:
Create requirements.txt:
Now anyone can install your exact dependencies:
pip install -r requirements.txtWriting Your First Test
Create tests/test_login.py:
Run it:
pytest tests/test_login.pyYou should see:
tests/test_login.py::test_successful_login PASSED
tests/test_login.py::test_login_validation PASSED
What Just Happened?
The page parameter is a pytest fixture provided by pytest-playwright. It automatically launches a browser, creates a page, and cleans up after the test.
The test uses Playwright's locator strategy. get_by_label finds inputs by their label text. get_by_role finds elements by their ARIA role. These locators are resilient. They don't break when you change CSS classes.
The @pytest.mark.smoke decorator lets you run subsets:
# Run only smoke tests
pytest -m smoke
# Run everything except smoke tests
pytest -m "not smoke"TypeScript Test Automation Setup
Environment Setup
TypeScript requires Node.js. Install it from nodejs.org if you haven't already.
# Create project directory
mkdir test-automation-typescript
cd test-automation-typescript
# Initialize npm project
npm init -y
# Install Playwright
npm init playwright@latestThe Playwright installer asks questions:
- Choose TypeScript
- Use
testsfor test directory - Add GitHub Actions workflow (we'll customize it later)
This creates everything you need.
Project Structure
test-automation-typescript/
├── tests/
│ └── login.spec.ts
├── playwright.config.ts
├── package.json
├── tsconfig.json
└── .github/
└── workflows/
└── playwright.yml
Configuration Files
The playwright.config.ts is already created. Key settings:
This runs tests on Chrome, Firefox, and Safari. In parallel. Automatically.
Writing Your First Test
Create tests/login.spec.ts:
Run it:
npx playwright testA test moves from your editor to local runs, then into the CI/CD pipeline on every push, where reports are generated and results reviewed.
You'll see tests run across all three browsers:
Running 6 tests using 3 workers
6 passed (12.3s)
To open last HTML report run:
npx playwright show-report
The TypeScript Advantage
TypeScript caught an error I almost made. I typed page.getByLable (missing the 'e'). My IDE showed a red squiggle immediately. Python wouldn't catch this until runtime.
The trade-off? More setup complexity. TypeScript requires a compiler, type definitions, and configuration. Python just runs.
Both examples above use a flat, single-file structure to keep your first test simple. Once a suite grows past a handful of tests, repeating locators and login steps in every file gets unwieldy, and most teams extract that logic into reusable classes with the Page Object Model pattern. The full companion repo examples linked above already follow that structure.
Running Tests in CI/CD
Tests that only run on your laptop aren't reliable. CI/CD runs them on every code change.
GitHub Actions Configuration
Both projects need a .github/workflows/tests.yml file.
For Python:
For TypeScript:
A single push or pull request fans the suite out across Chromium, Firefox, and Webkit in parallel, then converges into one uploaded report artifact.
Push your code. GitHub Actions runs your tests automatically. If they fail, your pull request gets blocked.
This prevents bugs from reaching production.
Test Reports and Debugging
When tests fail, you need context. Screenshots, videos, traces.
HTML Reports
Playwright generates beautiful HTML reports automatically.
Python: Add to pytest.ini:
[pytest]
addopts = --html=report.html --self-contained-htmlInstall the plugin:
pip install pytest-htmlTypeScript: Already configured. Just run:
npx playwright show-reportThe report opens with a pass/fail summary, then lists each test with its status indicator and execution time so you can jump straight to the failures.
The report shows:
- Which tests passed or failed
- Execution time per test
- Screenshots at failure point
- Full traces you can replay
Click a failed test. You see exactly what the browser saw when it failed.
Trace Viewer
Traces are time-travel debugging. Playwright records every action, screenshot, and network request.
When a test fails in CI/CD, download the trace artifact. Open it:
npx playwright show-trace trace.zipYou can step through the test action by action. See what the page looked like. Check network requests. Inspect the DOM at any point.
This is how you debug flaky tests. For the full playbook on why tests flake in the first place, see Chapter 6.
Parallel Execution
Running tests one at a time is slow. Run them in parallel.
Python: pytest-xdist handles this:
# Auto-detect CPU cores and use them all
pytest -n auto
# Use specific number of workers
pytest -n 4TypeScript: Playwright parallelizes by default:
// In playwright.config.ts
export default defineConfig({
workers: process.env.CI ? 1 : undefined, // All cores locally, 1 in CI
fullyParallel: true,
});My laptop has 8 cores. Without parallelization, 24 tests take 3 minutes. With parallelization? 30 seconds.
The catch: tests must be independent. If one test depends on another's data, parallel execution breaks them. Write tests that set up their own data.
Cross-Browser Testing
Your users don't all use Chrome. Test Firefox and Safari too.
Python: Configure in pytest.ini:
[pytest]
addopts = --browser chromium --browser firefox --browser webkitTypeScript: Already configured in playwright.config.ts. The projects array defines browsers:
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],Run tests:
# Python
pytest tests/
# TypeScript
npx playwright testPlaywright runs your tests on all three browsers. If a test passes on Chrome but fails on Firefox, you found a real browser compatibility bug.
Common Errors and Fixes
"Timeout waiting for element"
Your test looks for an element that never appears. Either the selector is wrong or the page is slow.
Fix: Increase timeout or use better selectors:
# Python - increase timeout
page.get_by_text("Loading...").wait_for(timeout=10000)
# TypeScript
await page.getByText('Loading...').waitFor({ timeout: 10000 });"Browser executable not found"
You forgot to install browser binaries.
Fix:
# Python
playwright install
# TypeScript
npx playwright install"Tests pass locally but fail in CI"
Usually timing issues. CI is slower than your laptop.
Fix: Add explicit waits:
# Wait for network to be idle
page.wait_for_load_state("networkidle")Or increase global timeout in CI:
// playwright.config.ts
export default defineConfig({
timeout: process.env.CI ? 60000 : 30000,
});Where to Go From Here
You have working test automation. Tests run locally and in CI/CD. You can run them in parallel across multiple browsers.
For complete starter projects with more examples, see our GitHub repository (Python and TypeScript templates ready to clone).
But there's still friction. You write tests manually. When your UI changes, tests break. You spend time maintaining selectors and fixing flaky tests.
Chapter 8 explores how AI changes everything. Autonomous testing that generates, runs, and maintains tests without human intervention. You're about to see how teams ship faster without hiring QA engineers.
Frequently Asked Questions
Both are excellent choices, and this chapter sets up Playwright in each. Pick Python with pytest if your team values simpler syntax and a faster start, which suits QA engineers coming from a testing background. Pick TypeScript if you want stronger IDE support, type safety, and alignment with a JavaScript codebase. The testing concepts, selectors, and CI/CD workflow are nearly identical across both, so the decision comes down to your team's existing skills.
For Python, yes. Playwright provides the browser automation, while pytest is the test runner that discovers your tests, executes them, and reports results. The pytest-playwright plugin connects the two and hands you the page fixture automatically. For TypeScript, Playwright ships with its own built-in test runner, so you do not need a separate one.
Add a GitHub Actions workflow under .github/workflows that installs your dependencies, installs the browser binaries with playwright install, and runs the suite on every push or pull request. This chapter includes ready-to-use workflow files for both Python and TypeScript, and both upload the HTML report as an artifact so you can inspect failures after the run.
The usual cause is timing. CI runners are slower than your laptop, so elements that appear instantly on your machine may still be loading in CI. Fix it by waiting on real conditions instead of fixed sleeps, for example page.wait_for_load_state('networkidle'), and by raising the global timeout when the CI environment variable is set. Missing browser binaries is the other common cause, which running playwright install resolves.
Run tests in parallel. Both pytest and Playwright distribute tests across multiple workers, so a suite that takes ten minutes serially can finish in two or three. Use pytest -n auto to match your CPU cores in Python, or set the workers option in playwright.config.ts for TypeScript. Keep each test independent so they can run in any order without sharing state.
Playwright supports all three engines out of the box. In TypeScript you declare them in the projects array of playwright.config.ts, and in Python you select them through pytest. The same test code runs against every engine, which is how you catch browser-specific bugs before your users do.
Start with the HTML report, which lists each test with its status and duration so you can jump straight to the failure. For a deeper look, open the Playwright trace with npx playwright show-trace, which replays the run step by step with DOM snapshots, network activity, and console logs. That usually shows exactly which action failed and why.
Yes. Hand-written Playwright and pytest suites break when selectors, labels, or layouts change, and keeping them green is ongoing work. That maintenance is the problem Chapter 8 addresses: Autonoma reads your codebase, decides what needs testing, and generates and self-heals the end-to-end tests, so coverage keeps up as your app evolves without you rewriting selectors.
Course Navigation
Chapter 7 of 8: Test Automation with Python and JavaScript ✓
Next Chapter →
Chapter 8: AI-Powered Software Testing with Autonoma
See how AI transforms testing. Learn about self-healing tests, tests generated straight from your codebase, and how teams cut maintenance time with autonomous testing.
← Previous Chapter
Chapter 6: How to Reduce Test Flakiness - Fix unreliable tests




