diff --git a/e2e/src/pages/ContactPage.ts b/e2e/src/pages/ContactPage.ts index f31363d..6960a3c 100644 --- a/e2e/src/pages/ContactPage.ts +++ b/e2e/src/pages/ContactPage.ts @@ -23,6 +23,11 @@ export class ContactPage extends BasePage { readonly phoneInfo: Locator; readonly emailInfo: Locator; + readonly nameError: Locator; + readonly emailError: Locator; + readonly phoneError: Locator; + readonly messageError: Locator; + constructor(page: Page) { super(page); this.url = '/contact'; @@ -44,6 +49,11 @@ export class ContactPage extends BasePage { this.addressInfo = this.contactInfoCard.locator('text=公司地址'); this.phoneInfo = this.contactInfoCard.locator('text=联系电话'); this.emailInfo = this.contactInfoCard.locator('text=电子邮箱'); + + this.nameError = page.locator('input[name="name"] + .error-message, input[name="name"] ~ .text-destructive').first(); + this.emailError = page.locator('input[name="email"] + .error-message, input[name="email"] ~ .text-destructive').first(); + this.phoneError = page.locator('input[name="phone"] + .error-message, input[name="phone"] ~ .text-destructive').first(); + this.messageError = page.locator('textarea[name="message"] + .error-message, textarea[name="message"] ~ .text-destructive').first(); } get breadcrumb(): Locator { @@ -336,4 +346,201 @@ export class ContactPage extends BasePage { } return workHours; } + + async getNameError(): Promise { + return await this.nameError.textContent() || ''; + } + + async getEmailError(): Promise { + return await this.emailError.textContent() || ''; + } + + async getPhoneError(): Promise { + return await this.phoneError.textContent() || ''; + } + + async getMessageError(): Promise { + return await this.messageError.textContent() || ''; + } + + async isNameErrorVisible(): Promise { + return await this.nameError.isVisible(); + } + + async isEmailErrorVisible(): Promise { + return await this.emailError.isVisible(); + } + + async isPhoneErrorVisible(): Promise { + return await this.phoneError.isVisible(); + } + + async isMessageErrorVisible(): Promise { + return await this.messageError.isVisible(); + } + + async testXSSInjection(payload: string): Promise { + await this.fillContactForm({ + name: payload, + email: 'test@example.com', + phone: '13800138000', + message: payload, + }); + await this.submitForm(); + } + + async testSQLInjection(payload: string): Promise { + await this.fillContactForm({ + name: payload, + email: payload, + phone: payload, + message: payload, + }); + await this.submitForm(); + } + + async testPathTraversal(payload: string): Promise { + await this.fillContactForm({ + name: payload, + email: 'test@example.com', + phone: '13800138000', + message: payload, + }); + await this.submitForm(); + } + + async verifyFormResponsiveLayout(viewport: { width: number; height: number }): Promise<{ + isFormVisible: boolean; + isSubmitButtonVisible: boolean; + isContactInfoVisible: boolean; + }> { + await this.page.setViewportSize(viewport); + await this.waitForTimeout(500); + + return { + isFormVisible: await this.isFormVisible(), + isSubmitButtonVisible: await this.isVisible(this.submitButton), + isContactInfoVisible: await this.isContactInfoCardVisible(), + }; + } + + async measureFormSubmissionPerformance(): Promise<{ + fillTime: number; + submitTime: number; + totalTime: number; + }> { + const startTime = Date.now(); + + const data = { + name: '测试用户', + email: 'test@example.com', + phone: '13800138000', + message: '这是一条测试消息', + }; + + const fillStartTime = Date.now(); + await this.fillContactForm(data); + const fillTime = Date.now() - fillStartTime; + + const submitStartTime = Date.now(); + await this.submitForm(); + await this.waitForFormSubmission(); + const submitTime = Date.now() - submitStartTime; + + const totalTime = Date.now() - startTime; + + return { + fillTime, + submitTime, + totalTime, + }; + } + + async getFormAccessibilityAttributes(): Promise<{ + nameAriaLabel: string | null; + emailAriaLabel: string | null; + phoneAriaLabel: string | null; + messageAriaLabel: string | null; + submitAriaLabel: string | null; + }> { + return { + nameAriaLabel: await this.nameInput.getAttribute('aria-label'), + emailAriaLabel: await this.emailInput.getAttribute('aria-label'), + phoneAriaLabel: await this.phoneInput.getAttribute('aria-label'), + messageAriaLabel: await this.messageInput.getAttribute('aria-label'), + submitAriaLabel: await this.submitButton.getAttribute('aria-label'), + }; + } + + async verifyFormLabels(): Promise<{ + nameLabel: string | null; + emailLabel: string | null; + phoneLabel: string | null; + messageLabel: string | null; + }> { + return { + nameLabel: await this.page.locator('label[for="name"]').textContent(), + emailLabel: await this.page.locator('label[for="email"]').textContent(), + phoneLabel: await this.page.locator('label[for="phone"]').textContent(), + messageLabel: await this.page.locator('label[for="message"]').textContent(), + }; + } + + async getFormInputTypes(): Promise<{ + nameType: string | null; + emailType: string | null; + phoneType: string | null; + subjectType: string | null; + }> { + return { + nameType: await this.nameInput.getAttribute('type'), + emailType: await this.emailInput.getAttribute('type'), + phoneType: await this.phoneInput.getAttribute('type'), + subjectType: await this.subjectInput.getAttribute('type'), + }; + } + + async verifyRequiredFields(): Promise<{ + nameRequired: boolean; + emailRequired: boolean; + phoneRequired: boolean; + messageRequired: boolean; + }> { + return { + nameRequired: await this.nameInput.getAttribute('required') !== null, + emailRequired: await this.emailInput.getAttribute('required') !== null, + phoneRequired: await this.phoneInput.getAttribute('required') !== null, + messageRequired: await this.messageInput.getAttribute('required') !== null, + }; + } + + async getFormAutocompleteAttributes(): Promise<{ + nameAutocomplete: string | null; + emailAutocomplete: string | null; + phoneAutocomplete: string | null; + }> { + return { + nameAutocomplete: await this.nameInput.getAttribute('autocomplete'), + emailAutocomplete: await this.emailInput.getAttribute('autocomplete'), + phoneAutocomplete: await this.phoneInput.getAttribute('autocomplete'), + }; + } + + async verifyKeyboardNavigation(): Promise { + await this.nameInput.focus(); + await this.pressKey('Tab'); + await this.pressKey('Tab'); + await this.pressKey('Tab'); + await this.pressKey('Tab'); + await this.pressKey('Tab'); + } + + async isFormKeyboardAccessible(): Promise { + try { + await this.verifyKeyboardNavigation(); + return true; + } catch { + return false; + } + } }