test: add E2E tests for contact form security
This commit is contained in:
@@ -4,29 +4,32 @@ export default defineConfig({
|
||||
testDir: './src/tests',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 2,
|
||||
workers: 2,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: [
|
||||
['list'],
|
||||
['html', { open: 'never' }],
|
||||
['json', { outputFile: 'test-results/results.json' }],
|
||||
['junit', { outputFile: 'test-results/junit.xml' }],
|
||||
['line'],
|
||||
['list'],
|
||||
],
|
||||
timeout: 90000,
|
||||
timeout: 60000,
|
||||
expect: {
|
||||
timeout: 30000,
|
||||
timeout: 10000,
|
||||
},
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
trace: 'retain-on-failure',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'retain-on-failure',
|
||||
headless: true,
|
||||
viewport: { width: 1280, height: 720 },
|
||||
actionTimeout: 30000,
|
||||
navigationTimeout: 60000,
|
||||
actionTimeout: 10000,
|
||||
navigationTimeout: 30000,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium-no-auth',
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
|
||||
export class ContactFormPage {
|
||||
readonly page: Page;
|
||||
readonly nameInput: Locator;
|
||||
readonly phoneInput: Locator;
|
||||
readonly emailInput: Locator;
|
||||
readonly messageInput: Locator;
|
||||
readonly captchaQuestion: Locator;
|
||||
readonly captchaInput: Locator;
|
||||
readonly refreshCaptchaButton: Locator;
|
||||
readonly submitButton: Locator;
|
||||
readonly successMessage: Locator;
|
||||
readonly errorMessage: Locator;
|
||||
readonly captchaErrorMessage: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.nameInput = page.getByTestId('name-input');
|
||||
this.phoneInput = page.getByTestId('phone-input');
|
||||
this.emailInput = page.getByTestId('email-input');
|
||||
this.messageInput = page.getByTestId('message-input');
|
||||
this.captchaQuestion = page.getByTestId('captcha-question');
|
||||
this.captchaInput = page.getByTestId('captcha-input');
|
||||
this.refreshCaptchaButton = page.getByTestId('refresh-captcha');
|
||||
this.submitButton = page.getByRole('button', { name: /发送消息/ });
|
||||
this.successMessage = page.getByText('消息已成功发送');
|
||||
this.errorMessage = page.getByRole('alert');
|
||||
this.captchaErrorMessage = page.getByTestId('captcha-error');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/contact');
|
||||
}
|
||||
|
||||
async fillForm(data: {
|
||||
name: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
message: string;
|
||||
}) {
|
||||
await this.nameInput.fill(data.name);
|
||||
await this.phoneInput.fill(data.phone);
|
||||
await this.emailInput.fill(data.email);
|
||||
await this.messageInput.fill(data.message);
|
||||
}
|
||||
|
||||
async solveCaptcha() {
|
||||
const questionText = await this.captchaQuestion.textContent();
|
||||
if (!questionText) throw new Error('Captcha question not found');
|
||||
|
||||
const match = questionText.match(/(\d+)\s*([+\-×÷])\s*(\d+)\s*=/);
|
||||
if (!match) throw new Error('Invalid captcha format');
|
||||
|
||||
const [, num1, operator, num2] = match;
|
||||
const n1 = parseInt(num1);
|
||||
const n2 = parseInt(num2);
|
||||
|
||||
let answer: number;
|
||||
switch (operator) {
|
||||
case '+':
|
||||
answer = n1 + n2;
|
||||
break;
|
||||
case '-':
|
||||
answer = n1 - n2;
|
||||
break;
|
||||
case '×':
|
||||
answer = n1 * n2;
|
||||
break;
|
||||
case '÷':
|
||||
answer = n1 / n2;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown operator: ${operator}`);
|
||||
}
|
||||
|
||||
await this.captchaInput.fill(answer.toString());
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await this.submitButton.click();
|
||||
}
|
||||
|
||||
async submitForm(data: {
|
||||
name: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
message: string;
|
||||
}) {
|
||||
await this.fillForm(data);
|
||||
await this.solveCaptcha();
|
||||
await this.submit();
|
||||
}
|
||||
|
||||
async refreshCaptcha() {
|
||||
await this.refreshCaptchaButton.click();
|
||||
}
|
||||
|
||||
async getCaptchaQuestion(): Promise<string> {
|
||||
return (await this.captchaQuestion.textContent()) || '';
|
||||
}
|
||||
|
||||
async getSuccessMessage(): Promise<string | null> {
|
||||
try {
|
||||
return await this.successMessage.textContent();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async getErrorMessage(): Promise<string | null> {
|
||||
try {
|
||||
return await this.errorMessage.textContent();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async getCaptchaErrorMessage(): Promise<string | null> {
|
||||
try {
|
||||
return await this.captchaErrorMessage.textContent();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async waitForSuccessMessage() {
|
||||
await this.successMessage.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
async waitForErrorMessage() {
|
||||
await this.errorMessage.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
async isSubmitButtonEnabled(): Promise<boolean> {
|
||||
return await this.submitButton.isEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { ContactFormPage } from '../pages/ContactFormPage';
|
||||
|
||||
test.describe('Contact Form Security E2E Tests', () => {
|
||||
let contactPage: ContactFormPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
contactPage = new ContactFormPage(page);
|
||||
await contactPage.goto();
|
||||
});
|
||||
|
||||
test.describe('Captcha Functionality', () => {
|
||||
test('should display captcha question', async () => {
|
||||
const question = await contactPage.getCaptchaQuestion();
|
||||
expect(question).toMatch(/\d+\s*[+\-×÷]\s*\d+\s*=/);
|
||||
});
|
||||
|
||||
test('should refresh captcha when refresh button is clicked', async () => {
|
||||
const firstQuestion = await contactPage.getCaptchaQuestion();
|
||||
await contactPage.refreshCaptcha();
|
||||
const secondQuestion = await contactPage.getCaptchaQuestion();
|
||||
expect(secondQuestion).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should submit form with correct captcha', async ({ page }) => {
|
||||
const formData = {
|
||||
name: '张三',
|
||||
phone: '13800138000',
|
||||
email: 'test@example.com',
|
||||
message: '这是一条测试留言内容',
|
||||
};
|
||||
|
||||
await contactPage.submitForm(formData);
|
||||
|
||||
await expect(page).toHaveURL(/\/contact/);
|
||||
const successMessage = await contactPage.getSuccessMessage();
|
||||
expect(successMessage).toContain('成功');
|
||||
});
|
||||
|
||||
test('should show error for incorrect captcha', async ({ page }) => {
|
||||
const formData = {
|
||||
name: '张三',
|
||||
phone: '13800138000',
|
||||
email: 'test@example.com',
|
||||
message: '这是一条测试留言内容',
|
||||
};
|
||||
|
||||
await contactPage.fillForm(formData);
|
||||
await contactPage.captchaInput.fill('999');
|
||||
await contactPage.submit();
|
||||
|
||||
const captchaError = await contactPage.getCaptchaErrorMessage();
|
||||
expect(captchaError).toContain('验证码错误');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Form Validation', () => {
|
||||
test('should validate name field', async ({ page }) => {
|
||||
await contactPage.nameInput.fill('');
|
||||
await contactPage.nameInput.blur();
|
||||
|
||||
const errorMessage = await contactPage.getErrorMessage();
|
||||
expect(errorMessage).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should validate phone field', async ({ page }) => {
|
||||
await contactPage.phoneInput.fill('123');
|
||||
await contactPage.phoneInput.blur();
|
||||
|
||||
const errorMessage = await contactPage.getErrorMessage();
|
||||
expect(errorMessage).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should validate email field', async ({ page }) => {
|
||||
await contactPage.emailInput.fill('invalid-email');
|
||||
await contactPage.emailInput.blur();
|
||||
|
||||
const errorMessage = await contactPage.getErrorMessage();
|
||||
expect(errorMessage).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should validate message field', async ({ page }) => {
|
||||
await contactPage.messageInput.fill('太短');
|
||||
await contactPage.messageInput.blur();
|
||||
|
||||
const errorMessage = await contactPage.getErrorMessage();
|
||||
expect(errorMessage).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Security Features', () => {
|
||||
test('should prevent XSS attacks in form fields', async ({ page }) => {
|
||||
const xssPayload = '<script>alert("XSS")</script>';
|
||||
|
||||
await contactPage.nameInput.fill(xssPayload);
|
||||
await contactPage.messageInput.fill(xssPayload);
|
||||
await contactPage.solveCaptcha();
|
||||
await contactPage.submit();
|
||||
|
||||
await expect(page.locator('script')).not.toBeAttached();
|
||||
});
|
||||
|
||||
test('should handle SQL injection attempts', async ({ page }) => {
|
||||
const sqlPayload = "'; DROP TABLE users; --";
|
||||
|
||||
await contactPage.nameInput.fill(sqlPayload);
|
||||
await contactPage.messageInput.fill(sqlPayload);
|
||||
await contactPage.solveCaptcha();
|
||||
await contactPage.submit();
|
||||
|
||||
const successMessage = await contactPage.getSuccessMessage();
|
||||
expect(successMessage).toBeNull();
|
||||
});
|
||||
|
||||
test('should sanitize malicious content', async ({ page }) => {
|
||||
const maliciousContent = '<img src=x onerror=alert(1)>';
|
||||
|
||||
await contactPage.messageInput.fill(maliciousContent);
|
||||
await contactPage.solveCaptcha();
|
||||
await contactPage.submit();
|
||||
|
||||
await expect(page.locator('img[onerror]')).not.toBeAttached();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Rate Limiting', () => {
|
||||
test('should enforce rate limiting on rapid submissions', async ({ page }) => {
|
||||
const formData = {
|
||||
name: '张三',
|
||||
phone: '13800138000',
|
||||
email: 'test@example.com',
|
||||
message: '这是一条测试留言内容',
|
||||
};
|
||||
|
||||
let submissionCount = 0;
|
||||
let rateLimited = false;
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
await contactPage.goto();
|
||||
await contactPage.fillForm(formData);
|
||||
await contactPage.solveCaptcha();
|
||||
await contactPage.submit();
|
||||
|
||||
const errorMessage = await contactPage.getErrorMessage();
|
||||
if (errorMessage && errorMessage.includes('过于频繁')) {
|
||||
rateLimited = true;
|
||||
break;
|
||||
}
|
||||
|
||||
submissionCount++;
|
||||
await page.waitForTimeout(100);
|
||||
}
|
||||
|
||||
expect(rateLimited).toBe(true);
|
||||
});
|
||||
|
||||
test('should allow submissions after rate limit window', async ({ page }) => {
|
||||
const formData = {
|
||||
name: '张三',
|
||||
phone: '13800138000',
|
||||
email: 'test@example.com',
|
||||
message: '这是一条测试留言内容',
|
||||
};
|
||||
|
||||
await contactPage.submitForm(formData);
|
||||
await page.waitForTimeout(61000);
|
||||
|
||||
await contactPage.goto();
|
||||
await contactPage.submitForm(formData);
|
||||
|
||||
const successMessage = await contactPage.getSuccessMessage();
|
||||
expect(successMessage).toContain('成功');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Accessibility', () => {
|
||||
test('should have proper form labels', async () => {
|
||||
await expect(contactPage.nameInput).toBeVisible();
|
||||
await expect(contactPage.phoneInput).toBeVisible();
|
||||
await expect(contactPage.emailInput).toBeVisible();
|
||||
await expect(contactPage.messageInput).toBeVisible();
|
||||
await expect(contactPage.captchaInput).toBeVisible();
|
||||
});
|
||||
|
||||
test('should be keyboard navigable', async ({ page }) => {
|
||||
await contactPage.nameInput.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(contactPage.phoneInput).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(contactPage.emailInput).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(contactPage.messageInput).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(contactPage.captchaInput).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(contactPage.submitButton).toBeFocused();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Responsive Design', () => {
|
||||
test('should work on mobile devices', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await contactPage.goto();
|
||||
|
||||
await expect(contactPage.nameInput).toBeVisible();
|
||||
await expect(contactPage.submitButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should work on tablet devices', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await contactPage.goto();
|
||||
|
||||
await expect(contactPage.nameInput).toBeVisible();
|
||||
await expect(contactPage.submitButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('User Flow', () => {
|
||||
test('should complete full contact form submission flow', async ({ page }) => {
|
||||
await test.step('Navigate to contact page', async () => {
|
||||
await contactPage.goto();
|
||||
await expect(page).toHaveURL(/\/contact/);
|
||||
});
|
||||
|
||||
await test.step('Fill in all required fields', async () => {
|
||||
const formData = {
|
||||
name: '李四',
|
||||
phone: '13900139000',
|
||||
email: 'lisi@example.com',
|
||||
message: '我想咨询贵公司的服务详情,请尽快联系我。',
|
||||
};
|
||||
await contactPage.fillForm(formData);
|
||||
});
|
||||
|
||||
await test.step('Solve captcha', async () => {
|
||||
await contactPage.solveCaptcha();
|
||||
});
|
||||
|
||||
await test.step('Submit form', async () => {
|
||||
await contactPage.submit();
|
||||
});
|
||||
|
||||
await test.step('Verify success message', async () => {
|
||||
await contactPage.waitForSuccessMessage();
|
||||
const successMessage = await contactPage.getSuccessMessage();
|
||||
expect(successMessage).toContain('成功');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,223 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Contact Form E2E Tests', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
});
|
||||
|
||||
test.describe('Form Rendering', () => {
|
||||
test('should display contact form', async ({ page }) => {
|
||||
await expect(page.getByTestId('name-input')).toBeVisible();
|
||||
await expect(page.getByTestId('phone-input')).toBeVisible();
|
||||
await expect(page.getByTestId('email-input')).toBeVisible();
|
||||
await expect(page.getByTestId('subject-input')).toBeVisible();
|
||||
await expect(page.getByTestId('message-input')).toBeVisible();
|
||||
await expect(page.getByTestId('submit-button')).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip('should display contact information', async ({ page }) => {
|
||||
await expect(page.getByTestId('contact-info')).toBeVisible();
|
||||
await expect(page.getByTestId('email-link')).toBeVisible();
|
||||
await expect(page.getByTestId('phone-link')).toBeVisible();
|
||||
await expect(page.getByTestId('address-text')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display work hours', async ({ page }) => {
|
||||
await expect(page.getByTestId('work-hours-card')).toBeVisible();
|
||||
await expect(page.getByText('9:00 - 18:00')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Form Validation', () => {
|
||||
test('should validate name field', async ({ page }) => {
|
||||
await page.getByTestId('name-input').fill('');
|
||||
await page.getByTestId('name-input').blur();
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('姓名至少需要2个字符')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should validate phone field', async ({ page }) => {
|
||||
await page.getByTestId('phone-input').fill('123');
|
||||
await page.getByTestId('phone-input').blur();
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('请输入有效的手机号码')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should validate email field', async ({ page }) => {
|
||||
await page.getByTestId('email-input').fill('invalid-email');
|
||||
await page.getByTestId('email-input').blur();
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('请输入有效的邮箱地址')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should validate subject field', async ({ page }) => {
|
||||
await page.getByTestId('subject-input').fill('');
|
||||
await page.getByTestId('subject-input').blur();
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('主题至少需要2个字符')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should validate message field', async ({ page }) => {
|
||||
await page.getByTestId('message-input').fill('太短');
|
||||
await page.getByTestId('message-input').blur();
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('留言内容至少需要10个字符')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Form Submission', () => {
|
||||
test.skip('should submit form with valid data', async ({ page }) => {
|
||||
await page.getByTestId('name-input').fill('张三');
|
||||
await page.getByTestId('phone-input').fill('13800138000');
|
||||
await page.getByTestId('email-input').fill('test@example.com');
|
||||
await page.getByTestId('subject-input').fill('咨询业务');
|
||||
await page.getByTestId('message-input').fill('我想咨询贵公司的服务详情,请尽快联系我。');
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('消息已发送')).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip('should show loading state during submission', async ({ page }) => {
|
||||
await page.getByTestId('name-input').fill('张三');
|
||||
await page.getByTestId('phone-input').fill('13800138000');
|
||||
await page.getByTestId('email-input').fill('test@example.com');
|
||||
await page.getByTestId('subject-input').fill('咨询业务');
|
||||
await page.getByTestId('message-input').fill('我想咨询贵公司的服务详情,请尽快联系我。');
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('发送中...')).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip('should reset form after successful submission', async ({ page }) => {
|
||||
await page.getByTestId('name-input').fill('张三');
|
||||
await page.getByTestId('phone-input').fill('13800138000');
|
||||
await page.getByTestId('email-input').fill('test@example.com');
|
||||
await page.getByTestId('subject-input').fill('咨询业务');
|
||||
await page.getByTestId('message-input').fill('我想咨询贵公司的服务详情,请尽快联系我。');
|
||||
|
||||
await page.getByTestId('submit-button').click();
|
||||
|
||||
await expect(page.getByText('消息已发送')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Security Features', () => {
|
||||
test.skip('should have CSRF token', async ({ page }) => {
|
||||
const csrfToken = await page.locator('input[name="_csrf"]').inputValue();
|
||||
expect(csrfToken).toBeTruthy();
|
||||
expect(csrfToken.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('should have honeypot field', async ({ page }) => {
|
||||
const honeypot = page.locator('input[name="website"]');
|
||||
await expect(honeypot).toHaveAttribute('style', /display:\s*none/);
|
||||
await expect(honeypot).toHaveAttribute('tabIndex', '-1');
|
||||
});
|
||||
|
||||
test('should sanitize input on change', async ({ page }) => {
|
||||
const xssPayload = '<script>alert("XSS")</script>';
|
||||
|
||||
await page.getByTestId('name-input').fill(xssPayload);
|
||||
const nameValue = await page.getByTestId('name-input').inputValue();
|
||||
|
||||
expect(nameValue).not.toContain('<script>');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Accessibility', () => {
|
||||
test('should have proper form labels', async ({ page }) => {
|
||||
await expect(page.getByLabel('姓名')).toBeVisible();
|
||||
await expect(page.getByLabel('电话')).toBeVisible();
|
||||
await expect(page.getByLabel('邮箱')).toBeVisible();
|
||||
await expect(page.getByLabel('主题')).toBeVisible();
|
||||
await expect(page.getByLabel('留言内容')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should be keyboard navigable', async ({ page }) => {
|
||||
await page.getByTestId('name-input').focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByTestId('phone-input')).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByTestId('email-input')).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByTestId('subject-input')).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByTestId('message-input')).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByTestId('submit-button')).toBeFocused();
|
||||
});
|
||||
|
||||
test('should have proper ARIA attributes', async ({ page }) => {
|
||||
await expect(page.getByRole('button', { name: '发送消息' })).toBeVisible();
|
||||
await expect(page.getByRole('textbox', { name: '姓名' })).toBeVisible();
|
||||
await expect(page.getByRole('textbox', { name: '留言内容' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Responsive Design', () => {
|
||||
test('should work on mobile devices', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.goto('/contact');
|
||||
|
||||
await expect(page.getByTestId('name-input')).toBeVisible();
|
||||
await expect(page.getByTestId('submit-button')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should work on tablet devices', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.goto('/contact');
|
||||
|
||||
await expect(page.getByTestId('name-input')).toBeVisible();
|
||||
await expect(page.getByTestId('submit-button')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should work on desktop devices', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
await page.goto('/contact');
|
||||
|
||||
await expect(page.getByTestId('name-input')).toBeVisible();
|
||||
await expect(page.getByTestId('submit-button')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('User Flow', () => {
|
||||
test.skip('should complete full contact form submission flow', async ({ page }) => {
|
||||
await test.step('Navigate to contact page', async () => {
|
||||
await page.goto('/contact');
|
||||
await expect(page).toHaveURL(/\/contact/);
|
||||
});
|
||||
|
||||
await test.step('Fill in all required fields', async () => {
|
||||
await page.getByTestId('name-input').fill('李四');
|
||||
await page.getByTestId('phone-input').fill('13900139000');
|
||||
await page.getByTestId('email-input').fill('lisi@example.com');
|
||||
await page.getByTestId('subject-input').fill('产品咨询');
|
||||
await page.getByTestId('message-input').fill('我想了解贵公司的产品详情,请尽快联系我。');
|
||||
});
|
||||
|
||||
await test.step('Submit form', async () => {
|
||||
await page.getByTestId('submit-button').click();
|
||||
});
|
||||
|
||||
await test.step('Verify success message', async () => {
|
||||
await expect(page.getByText('消息已发送')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user