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

FeatureTaikoSeleniumPlaywright
Selector StrategySmart, natural languageManual (ID, CSS, XPath)Advanced (text, role, label)
REPL ModeBuilt-inNoNo
Learning CurveVery LowMedium-HighMedium
Auto-waitIntelligentManualBuilt-in
Browser SupportChrome, Firefox, SafariAll major browsersChromium, Firefox, WebKit
Multi-tabYesYesExcellent
Network InterceptYesLimitedExcellent
ScreenshotsEasyManualBuilt-in
Video RecordingPluginNoBuilt-in
Code GenerationREPL .codeNoCodegen tool
Mobile TestingLimitedYes (Appium)Limited
Parallel ExecutionManualYesBuilt-in
CommunityGrowingVery LargeLarge

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:

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.