TL;DR
- Taiko uses natural language selectors:
click('Login')instead ofpage.locator('#btn-login').click()- Built-in REPL mode for interactive test development — type
.codeto generate script from your session- Best for: teams prioritizing simplicity over advanced features; rapid prototyping
Best for: Teams new to browser automation or frustrated with flaky XPath/CSS selectors Skip if: You need parallel execution, complex multi-browser scenarios, or mobile testing Read time: 10 minutes
Your Selenium tests fail because a developer renamed a CSS class. Your Playwright selectors break when the DOM structure changes. What if your tests could find elements the way humans do — by the text they see?
Taiko takes a different approach to browser automation. Instead of brittle locators, you write click('Submit') and Taiko figures out which button you mean.
What Makes Taiko Different
Taiko is a free and open-source browser automation tool from ThoughtWorks, designed with smart selectors and a human-readable API. The key difference from Selenium or Playwright: you describe what you see, not where it is in the DOM.
Smart Selectors: Natural Language Automation
// Traditional Selenium/Playwright
await page.locator('#username').fill('user@example.com');
await page.locator('xpath=//button[contains(text(), "Login")]').click();
// Taiko - Smart Selectors
await write('user@example.com', into(textBox('Username')));
await click('Login');
// More examples
await write('John Doe', into('Full Name'));
await click(button('Submit'));
await click(link('Contact Us'));
await select('California', from('State'));
await attach('resume.pdf', to('Upload Resume'));
REPL Mode: Interactive Testing
# Start Taiko REPL
$ taiko
> openBrowser()
✔ Browser opened
> goto('https://example.com')
✔ Navigated to URL https://example.com
> write('search query', into('Search'))
✔ Wrote search query into the Search
> click('Search')
✔ Clicked element matching text "Search"
> screenshot()
✔ Screenshot is created at /screenshots/1234567890.png
> .code
// Code generated for this session:
const { openBrowser, goto, write, click, screenshot } = require('taiko');
(async () => {
try {
await openBrowser();
await goto('https://example.com');
await write('search query', into('Search'));
await click('Search');
await screenshot();
} catch (error) {
console.error(error);
} finally {
await closeBrowser();
}
})();
> closeBrowser()
✔ Browser closed
Complete Test Example
// test/login.test.js
const { openBrowser, goto, write, click, text, closeBrowser, into, button } = require('taiko');
describe('User Authentication', () => {
beforeAll(async () => {
await openBrowser({ headless: true });
});
afterAll(async () => {
await closeBrowser();
});
test('should login with valid credentials', async () => {
await goto('https://example.com/login');
await write('user@example.com', into('Email'));
await write('password123', into('Password'));
await click(button('Login'));
await text('Welcome back').exists();
});
test('should show error for invalid credentials', async () => {
await goto('https://example.com/login');
await write('invalid@example.com', into('Email'));
await write('wrongpass', into('Password'));
await click(button('Login'));
await text('Invalid credentials').exists();
});
});
Advanced Features
Proximity Selectors
// Find elements relative to others
await write('John', into(textBox(near('First Name'))));
await write('Doe', into(textBox(near('Last Name'))));
await click(button('Save', below('Profile Picture')));
await click(link('Edit', toRightOf('John Doe')));
// Chaining proximity
await write('94105', into(
textBox(near('Zip Code'), below('Address'))
));
Custom Waits and Assertions
const { openBrowser, goto, click, waitFor, text, evaluate } = require('taiko');
// Wait for element
await waitFor('Loading complete');
await waitFor(2000); // Wait 2 seconds
await waitFor(async () => (await $('div.loader').exists()) === false);
// Custom assertions
await text('Order Confirmed').exists();
await text('Error').exists(undefined, { timeout: 1000 }).catch(() => {
console.log('No error message shown');
});
// Evaluate JavaScript
const count = await evaluate(() => {
return document.querySelectorAll('.product-item').length;
});
console.log(`Found ${count} products`);
Intercept Network Requests
const { openBrowser, goto, intercept, click } = require('taiko');
// Intercept and modify requests
await intercept('https://api.example.com/products', {
body: JSON.stringify({
products: [
{ id: 1, name: 'Test Product', price: 99.99 }
]
})
});
await goto('https://example.com/products');
// Page will receive mocked response
// Intercept and validate
await intercept('https://api.example.com/orders', (request) => {
console.log('Order request:', request.postData);
return { status: 201, body: { orderId: '12345' } };
});
await click('Place Order');
Taiko vs Selenium vs Playwright
| Feature | Taiko | Selenium | Playwright |
|---|---|---|---|
| Selector Strategy | Smart, natural language | Manual (ID, CSS, XPath) | Advanced (text, role, label) |
| REPL Mode | Built-in | No | No |
| Learning Curve | Very Low | Medium-High | Medium |
| Auto-wait | Intelligent | Manual | Built-in |
| Browser Support | Chrome, Firefox, Safari | All major browsers | Chromium, Firefox, WebKit |
| Multi-tab | Yes | Yes | Excellent |
| Network Intercept | Yes | Limited | Excellent |
| Screenshots | Easy | Manual | Built-in |
| Video Recording | Plugin | No | Built-in |
| Code Generation | REPL .code | No | Codegen tool |
| Mobile Testing | Limited | Yes (Appium) | Limited |
| Parallel Execution | Manual | Yes | Built-in |
| Community | Growing | Very Large | Large |
Headless Testing
const { openBrowser, goto, screenshot, closeBrowser } = require('taiko');
(async () => {
try {
// Headless mode
await openBrowser({ headless: true });
await goto('https://example.com');
// Headless with custom args
await openBrowser({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage'
]
});
await screenshot({ fullPage: true });
} finally {
await closeBrowser();
}
})();
CI/CD Integration
# .github/workflows/taiko-tests.yml
name: Taiko E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run Taiko tests
run: npm test
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: taiko-screenshots
path: screenshots/
Best Practices
1. Page Object Pattern with Taiko
// pages/LoginPage.js
const { goto, write, click, into, button } = require('taiko');
class LoginPage {
async navigate() {
await goto('https://example.com/login');
}
async login(email, password) {
await write(email, into('Email'));
await write(password, into('Password'));
await click(button('Login'));
}
async loginAsAdmin() {
await this.login('admin@example.com', 'admin123');
}
}
module.exports = new LoginPage();
2. Reusable Helpers
// helpers/auth.js
const { write, click, into, button } = require('taiko');
async function loginAs(userType) {
const users = {
admin: { email: 'admin@example.com', password: 'admin123' },
user: { email: 'user@example.com', password: 'user123' }
};
const credentials = users[userType];
await write(credentials.email, into('Email'));
await write(credentials.password, into('Password'));
await click(button('Login'));
}
module.exports = { loginAs };
3. Custom Assertions
// helpers/assertions.js
const { text } = require('taiko');
async function assertTextExists(expectedText, message) {
const exists = await text(expectedText).exists();
if (!exists) {
throw new Error(message || `Text "${expectedText}" not found`);
}
}
async function assertTextNotExists(unexpectedText) {
const exists = await text(unexpectedText).exists();
if (exists) {
throw new Error(`Text "${unexpectedText}" should not exist`);
}
}
module.exports = { assertTextExists, assertTextNotExists };
AI-Assisted Test Development with Taiko
AI tools work well with Taiko’s natural language approach.
What AI does well:
- Converting manual test descriptions to Taiko scripts (natural language in, natural language out)
- Generating proximity selector chains for complex forms
- Creating test data sets for data-driven testing
- Explaining Taiko’s API functions and their Selenium/Playwright equivalents
What still needs humans:
- Deciding which user flows need automation
- Determining appropriate assertions for business logic
- Debugging timing issues in dynamic applications
- Choosing between Taiko’s multiple selector strategies for ambiguous elements
Useful prompt:
Convert this manual test case to a Taiko script:
1. Open the login page
2. Enter username in the Username field
3. Enter password in the Password field
4. Click the Login button
5. Verify "Welcome back" text appears
Use Taiko's smart selectors and proper error handling.
FAQ
What is Taiko used for?
Taiko is a Node.js browser automation tool from ThoughtWorks designed for web testing. Its key feature is smart selectors — you write click('Login') instead of finding CSS selectors or XPath. This makes tests more readable and less brittle when UI changes. Taiko is used for E2E testing, regression testing, and rapid prototyping of browser automation scripts.
Is Taiko better than Playwright?
They serve different needs. Taiko excels at simplicity: the REPL mode lets you build tests interactively, and smart selectors reduce maintenance burden. Playwright offers more features: parallel execution, multiple browser contexts, video recording, and trace viewer for debugging. Choose Taiko for small to medium projects prioritizing developer experience. Choose Playwright for large test suites needing advanced features.
Does Taiko support headless testing?
Yes. Use openBrowser({ headless: true }) for headless mode. This is essential for CI/CD pipelines where no display is available. Taiko also supports custom browser arguments for Docker environments:
await openBrowser({
headless: true,
args: ['--no-sandbox', '--disable-dev-shm-usage']
});
Can Taiko intercept network requests?
Yes. Taiko’s intercept() function mocks API responses without hitting the backend:
await intercept('https://api.example.com/users', {
body: JSON.stringify([{ id: 1, name: 'Test User' }])
});
This is similar to Playwright’s page.route() or Cypress cy.intercept(). Useful for testing error states, slow network conditions, or isolating frontend tests from backend dependencies.
When to Choose Taiko
Choose Taiko when:
- Team values simplicity and ease of use
- Natural language selectors preferred
- REPL-driven development is attractive
- Chromium/Firefox/Safari coverage sufficient
- Rapid prototyping needed
Choose alternatives when:
- Need advanced features like trace viewer (Playwright)
- Large existing test infrastructure in another framework
- Mobile testing essential (Appium)
- Built-in parallel execution needed (Playwright)
- Extensive browser version matrix required (Selenium Grid)
For projects where developer experience and test maintainability are priorities, Taiko’s smart selectors and intuitive API provide a compelling alternative to traditional automation frameworks.
Official Resources
See Also
- Playwright Comprehensive Guide - Multi-browser testing with Playwright for teams needing advanced features
- Cypress Deep Dive - Architecture, debugging, and network stubbing for component-heavy apps
- Selenium WebDriver 2025 - When Selenium is still the right choice
- Gauge Framework Guide - Combine Taiko with Gauge for BDD-style specifications
