Automation testing

[Playwright] Tất tần tật về input text

Phong Do Phong Do · 06/09/2025 · 12 phút đọc

Trong quá trình automation testing, việc xử lý các trường nhập liệu văn bản (input) là một trong những thao tác cơ bản nhưng cực kỳ quan trọng. Playwright cung cấp nhiều phương thức mạnh mẽ để làm việc với text input, từ những thao tác đơn giản đến xử lý các tình huống phức tạp. Bài viết này sẽ hướng dẫn chi tiết cách xử lý input dạng text trong Playwright TypeScript.

Các phương thức cơ bản

1. Fill – Phương thức phổ biến nhất

Phương thức fill() là cách nhanh nhất và đơn giản nhất để nhập văn bản vào một trường input. Nó sẽ xóa toàn bộ nội dung hiện tại và thay thế bằng văn bản mới.

import { test, expect } from '@playwright/test';

test('Nhập văn bản với fill()', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  // Nhập văn bản vào trường username
  await page.fill('#username', 'testuser@example.com');
  
  // Hoặc sử dụng với locator
  const emailInput = page.locator('input[type="email"]');
  await emailInput.fill('admin@company.com');
  
  // Verify giá trị đã nhập
  await expect(emailInput).toHaveValue('admin@company.com');
});

2. Type – Mô phỏng thao tác gõ phím

Khác với fill(), phương thức type() mô phỏng việc gõ từng ký tự một, phù hợp khi cần test các sự kiện keyboard hoặc auto-complete.

test('Nhập văn bản với type()', async ({ page }) => {
  await page.goto('https://example.com/search');
  
  const searchBox = page.locator('#search-input');
  
  // Gõ từng ký tự với độ trễ 100ms
  await searchBox.type('Playwright testing', { delay: 100 });
  
  // Type() không xóa nội dung cũ, cần clear trước nếu muốn
  await searchBox.clear();
  await searchBox.type('New search term');
});

3. PressSequentially – Phương thức mới trong Playwright

pressSequentially() là phương thức được giới thiệu gần đây, tương tự như type() nhưng với nhiều tùy chọn hơn.

test('Sử dụng pressSequentially()', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const input = page.locator('#dynamic-input');
  
  // Nhập với độ trễ giữa các ký tự
  await input.pressSequentially('Test automation', {
    delay: 50 // milliseconds giữa mỗi ký tự
  });
  
  // Kết hợp với các phím đặc biệt
  await input.pressSequentially('Hello');
  await page.keyboard.press('Control+A'); // Select all
  await input.pressSequentially('Replaced text');
});

Xử lý các tình huống đặc biệt

1. Clear và nhập lại văn bản

test('Clear và nhập văn bản mới', async ({ page }) => {
  await page.goto('https://example.com/profile');
  
  const nameInput = page.locator('#name');
  
  // Cách 1: Dùng clear()
  await nameInput.clear();
  await nameInput.fill('John Doe');
  
  // Cách 2: Dùng fill() trực tiếp (tự động clear)
  await nameInput.fill('Jane Smith');
  
  // Cách 3: Select all và ghi đè
  await nameInput.click();
  await page.keyboard.press('Control+A');
  await nameInput.type('New Name');
});

2. Xử lý input có validation

test('Xử lý input với validation', async ({ page }) => {
  await page.goto('https://example.com/register');
  
  const passwordInput = page.locator('#password');
  const errorMessage = page.locator('.error-message');
  
  // Nhập password không hợp lệ
  await passwordInput.fill('123');
  await passwordInput.blur(); // Trigger validation
  
  // Verify error message xuất hiện
  await expect(errorMessage).toBeVisible();
  await expect(errorMessage).toContainText('Password must be at least 8 characters');
  
  // Nhập password hợp lệ
  await passwordInput.fill('SecureP@ssw0rd');
  await expect(errorMessage).not.toBeVisible();
});

3. Xử lý input với mask hoặc format

test('Xử lý input có mask (phone, date)', async ({ page }) => {
  await page.goto('https://example.com/contact');
  
  // Phone number với mask
  const phoneInput = page.locator('#phone');
  await phoneInput.fill('1234567890');
  
  // Verify format được apply
  const formattedValue = await phoneInput.inputValue();
  expect(formattedValue).toBe('(123) 456-7890');
  
  // Date input
  const dateInput = page.locator('input[type="date"]');
  await dateInput.fill('2024-12-25');
  
  // Credit card với spaces
  const cardInput = page.locator('#credit-card');
  await cardInput.type('4111111111111111');
  const cardValue = await cardInput.inputValue();
  expect(cardValue).toBe('4111 1111 1111 1111');
});

Kỹ thuật nâng cao

1. Xử lý auto-complete và dropdown suggestions

test('Xử lý auto-complete', async ({ page }) => {
  await page.goto('https://example.com/location');
  
  const cityInput = page.locator('#city-search');
  
  // Gõ từ khóa để trigger auto-complete
  await cityInput.type('New');
  
  // Đợi dropdown suggestions xuất hiện
  await page.waitForSelector('.autocomplete-suggestions');
  
  // Chọn option từ suggestions
  await page.locator('.suggestion-item:has-text("New York")').click();
  
  // Verify giá trị được chọn
  await expect(cityInput).toHaveValue('New York, NY, USA');
});

2. Upload file qua input

test('Upload file', async ({ page }) => {
  await page.goto('https://example.com/upload');
  
  // Upload single file
  const fileInput = page.locator('input[type="file"]');
  await fileInput.setInputFiles('path/to/document.pdf');
  
  // Upload multiple files
  await fileInput.setInputFiles([
    'path/to/file1.jpg',
    'path/to/file2.png'
  ]);
  
  // Clear selected files
  await fileInput.setInputFiles([]);
});

3. Xử lý contenteditable elements

test('Xử lý contenteditable', async ({ page }) => {
  await page.goto('https://example.com/editor');
  
  const editor = page.locator('[contenteditable="true"]');
  
  // Click vào editor
  await editor.click();
  
  // Clear content hiện tại
  await page.keyboard.press('Control+A');
  await page.keyboard.press('Delete');
  
  // Nhập nội dung mới
  await editor.type('This is rich text content');
  
  // Format text
  await page.keyboard.press('Control+A');
  await page.keyboard.press('Control+B'); // Bold
  
  // Verify content
  const content = await editor.innerText();
  expect(content).toBe('This is rich text content');
});

Best Practices và Tips

1. Waiting strategies cho input

test('Waiting strategies', async ({ page }) => {
  await page.goto('https://example.com/dynamic-form');
  
  // Đợi input enabled trước khi nhập
  const input = page.locator('#delayed-input');
  await input.waitFor({ state: 'visible' });
  await expect(input).toBeEnabled();
  await input.fill('Test data');
  
  // Đợi sau khi nhập để đảm bảo value được update
  await page.waitForTimeout(500); // Tránh dùng, chỉ khi cần thiết
  
  // Better: Đợi một điều kiện cụ thể
  await expect(input).toHaveValue('Test data');
});

2. Error handling

test('Error handling cho input', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  try {
    // Set timeout ngắn cho input cụ thể
    await page.locator('#non-existent').fill('test', { timeout: 5000 });
  } catch (error) {
    console.log('Input không tồn tại hoặc không thể nhập');
    // Handle error hoặc use alternative approach
  }
  
  // Retry mechanism
  const retryFill = async (selector: string, text: string, retries = 3) => {
    for (let i = 0; i < retries; i++) {
      try {
        await page.fill(selector, text);
        return;
      } catch (error) {
        if (i === retries - 1) throw error;
        await page.waitForTimeout(1000);
      }
    }
  };
  
  await retryFill('#flaky-input', 'Test text');
});
  • Tìm hiểu thêm: dùng expect.poll và expect.toPass

3. Validation helpers

test('Validation helpers', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  // Helper function để validate input
  const validateAndFill = async (
    selector: string, 
    value: string, 
    expectedPattern?: RegExp
  ) => {
    const input = page.locator(selector);
    await input.fill(value);
    
    if (expectedPattern) {
      const actualValue = await input.inputValue();
      expect(actualValue).toMatch(expectedPattern);
    }
    
    return input;
  };
  
  // Sử dụng helper
  await validateAndFill('#email', 'test@example.com', /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
  await validateAndFill('#phone', '1234567890', /^\d{10}$/);
});

Tìm hiểu thêm

Phong Do
Tác giả
Phong Do