Introduction to Taiko
Taiko is a free and open-source browser automation tool from ThoughtWorks, designed with smart selectors and a human-readable API. Unlike traditional tools requiring complex XPath or CSS selectors, Taiko uses natural language-like commands with intelligent element detection.
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 };
Conclusion
Taiko provides a refreshingly simple approach to browser automation with its smart selectors and REPL-driven development. While it may lack some advanced features of Playwright (as discussed in Cypress Deep Dive: Architecture, Debugging, and Network Stubbing Mastery) or the extensive browser support of Selenium, its ease of use and natural language API make it ideal for teams prioritizing developer experience and rapid test development.
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 (Playwright)
- (as discussed in Playwright Comprehensive Guide: Multi-Browser Testing, Auto-Wait, and Trace Viewer Mastery) Extensive browser support required (Selenium)
- Large existing test infrastructure
- Mobile testing essential
- Built-in parallel execution needed
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.