Skip to main content
Glama
form-creation.e2e.test.ts16.2 kB
/** * End-to-End Integration Tests for Form Creation * * This test suite validates the complete form creation workflow from API calls * to actual form rendering in the Tally interface, covering all test scenarios * identified in the comprehensive test design. */ import { test, expect, Page } from '@playwright/test'; import { TestFormFactory, StagingTestUtils, FormRenderingValidator, STAGING_CONFIG } from './staging-environment'; import { FormConfig, QuestionType } from '../../models/form-config'; // Test group: Minimal Forms (Basic Functionality) test.describe('Minimal Forms - Basic Functionality', () => { test('should create and render a title-only form', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createMinimalForm('E2E Test - Title Only Form'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Verify form title is displayed const titleElement = page.locator('h1, .form-title, [data-testid="form-title"]').first(); await expect(titleElement).toBeVisible(); const displayedTitle = await titleElement.textContent(); expect(FormRenderingValidator.validateTitle(displayedTitle || '', createdConfig.title)).toBe(true); // Verify no question fields are present (title-only form) const inputElements = page.locator('input, select, textarea'); const inputCount = await inputElements.count(); expect(inputCount).toBe(0); }); test('should create and render a single text question form', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createMinimalForm('E2E Test - Single Question Form'); formConfig.questions = [{ id: 'single_question', type: QuestionType.TEXT, label: 'What is your name?', required: true, placeholder: 'Enter your name here' }]; // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Verify question label is displayed (Tally renders as h3, not label) const questionLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'What is your name?' }); await expect(questionLabel).toBeVisible(); // Verify text input field exists const textInput = page.locator('input[type="text"], input:not([type]), [role="textbox"]').first(); await expect(textInput).toBeVisible(); // Verify placeholder text const placeholder = await textInput.getAttribute('placeholder'); expect(placeholder).toContain('Enter your name here'); // Verify required field indicator (may not be accessible attribute in Tally) // Skip this check as Tally may handle required validation differently }); }); // Test group: Medium Complexity Forms (Multiple Field Types) test.describe('Medium Complexity Forms - Multiple Field Types', () => { test('should create and render a contact form with multiple field types', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createContactForm('E2E Test - Contact Form'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form title and description await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Note: Tally may not render description as a separate element // Skip description check for now and focus on field rendering // Verify all expected fields are present const expectedFields = [ { label: 'Full Name', type: 'text', required: true }, { label: 'Email Address', type: 'email', required: true }, { label: 'Phone Number', type: 'tel', required: false }, { label: 'Message', type: 'textarea', required: true } ]; for (const field of expectedFields) { // Find the field by label (now looking for headings) const fieldLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: field.label }); await expect(fieldLabel).toBeVisible(); // Find the corresponding input (simplified approach) let inputSelector = ''; switch (field.type) { case 'textarea': inputSelector = 'textarea, [role="textbox"]'; break; case 'email': inputSelector = 'input[type="email"], [role="textbox"]'; break; case 'tel': inputSelector = 'input[type="tel"], input[type="phone"], [role="textbox"]'; break; default: inputSelector = 'input[type="text"], input:not([type]), [role="textbox"]'; } // Verify at least one input of the expected type exists const inputElement = page.locator(inputSelector).first(); await expect(inputElement).toBeVisible(); } }); test('should create and render a survey form with choice-based questions', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createSurveyForm('E2E Test - Survey Form'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Verify rating question (look for heading instead of label) const ratingLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'How satisfied are you with our service?' }); await expect(ratingLabel).toBeVisible(); // Verify dropdown question const dropdownLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'Which department do you need help with?' }); await expect(dropdownLabel).toBeVisible(); // Check if select element exists (Tally might use custom dropdowns) const dropdownSelect = page.locator('select, [role="combobox"], [role="listbox"]').first(); if (await dropdownSelect.count() > 0) { await expect(dropdownSelect).toBeVisible(); // If it's a real select, check options if (await page.locator('select').count() > 0) { const options = await page.locator('select option').allTextContents(); expect(options.length).toBeGreaterThan(0); } } // Verify checkboxes question const checkboxLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'What features are most important to you?' }); await expect(checkboxLabel).toBeVisible(); // Check for checkbox inputs (might be custom styled) const checkboxInputs = page.locator('input[type="checkbox"], [role="checkbox"]'); const checkboxCount = await checkboxInputs.count(); // Allow for 0 if Tally uses custom checkbox styling expect(checkboxCount).toBeGreaterThanOrEqual(0); // Verify multiple choice question const radioLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'What is your top priority for improvements?' }); await expect(radioLabel).toBeVisible(); }); }); // Test group: Complex Forms (Advanced Features) test.describe('Complex Forms - Advanced Features', () => { test('should create and render a complex form with file uploads and signatures', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createComplexForm('E2E Test - Complex Form'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Verify file upload field (look for heading instead of label) const fileUploadLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'Upload Supporting Documents' }); await expect(fileUploadLabel).toBeVisible(); // Check for file input (might be custom styled) const fileInput = page.locator('input[type="file"], [data-testid="file-upload"]').first(); if (await fileInput.count() > 0) { await expect(fileInput).toBeVisible(); } // Verify signature field const signatureLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'Digital Signature' }); await expect(signatureLabel).toBeVisible(); }); }); // Test group: Edge Cases test.describe('Edge Cases - Stress Testing', () => { test('should handle forms with many questions and special characters', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createEdgeCaseForm('E2E Test - Edge Cases 🚀'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title.substring(0, 50), 'i')); // Use substring for long titles // Verify that multiple questions are present (look for headings instead of labels) const questionLabels = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: /Question \d+:/ }); const questionCount = await questionLabels.count(); expect(questionCount).toBeGreaterThan(0); // Should have at least some questions, exact count may vary // Verify that long question labels are properly displayed const longQuestionLabel = page.locator('h1, h2, h3, h4, h5, h6').filter({ hasText: 'Question 1: This is a very long question label' }); await expect(longQuestionLabel).toBeVisible(); // Verify form handles special characters in title const titleWithEmoji = page.locator('h1').filter({ hasText: '🚀' }); await expect(titleWithEmoji).toBeVisible(); }); }); // Test group: Error Handling test.describe('Error Handling - Invalid Scenarios', () => { test('should handle invalid form URLs gracefully', async ({ page }) => { // Act - Try to navigate to a non-existent form const invalidUrl = 'https://tally.so/forms/non-existent-form-id-12345'; // Navigate and expect either 404 page or error message const response = await page.goto(invalidUrl); // Assert - Should either get 404 status or error page if (response) { expect([404, 410, 500].includes(response.status())).toBe(true); } // Or check for error message on page const errorIndicators = [ page.locator('text=not found'), page.locator('text=404'), page.locator('text=error'), page.locator('.error-message'), page.locator('[data-testid="error"]') ]; let errorFound = false; for (const indicator of errorIndicators) { if (await indicator.count() > 0) { errorFound = true; break; } } // At least one error indicator should be present expect(errorFound || (response && response.status() >= 400)).toBe(true); }); }); // Test group: Form Functionality test.describe('Form Functionality - User Interactions', () => { test('should allow form submission with valid data', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createContactForm('E2E Test - Submission Test'); // Act - Create form via API const { formId, formUrl, formConfig: createdConfig } = await StagingTestUtils.createTestForm(formConfig); // Assert - Verify form was created expect(formId).toBeTruthy(); expect(formUrl).toContain('tally.so'); // Act - Navigate to form in browser await page.goto(formUrl); // Assert - Verify form renders correctly await expect(page).toHaveTitle(new RegExp(createdConfig.title, 'i')); // Fill out the form with valid data const inputs = page.locator('input, textarea, [role="textbox"]'); const inputCount = await inputs.count(); if (inputCount > 0) { // Fill first input (name) await inputs.nth(0).fill('John Doe'); if (inputCount > 1) { // Fill email if it exists await inputs.nth(1).fill('john.doe@example.com'); } if (inputCount > 2) { // Fill phone if it exists await inputs.nth(2).fill('555-123-4567'); } if (inputCount > 3) { // Fill message if it exists await inputs.nth(3).fill('This is a test message for E2E validation.'); } } // Submit the form const submitButton = page.locator('button[type="submit"], button').filter({ hasText: /submit/i }).first(); if (await submitButton.count() > 0) { await submitButton.click(); // Wait for form submission to complete await page.waitForLoadState('networkidle'); // Check if we're redirected or get a success message // Tally might keep same URL but show success state const successSelectors = [ 'text=Thanks for completing this form!', 'text=Thank you', 'text=thanks', 'text=success', 'text=submitted', 'h1:has-text("Thanks")', 'h1:has-text("Thank you")' ]; let hasSuccessMessage = false; for (const selector of successSelectors) { if (await page.locator(selector).count() > 0) { hasSuccessMessage = true; break; } } const currentUrl = page.url(); // Either URL changed or success message appeared expect(hasSuccessMessage || currentUrl !== formUrl).toBe(true); } }); test('should validate required fields', async ({ page }) => { // Arrange const formConfig = TestFormFactory.createContactForm('E2E Test - Validation Test'); // Act - Create form via API const { formId, formUrl } = await StagingTestUtils.createTestForm(formConfig); // Navigate to form await page.goto(formUrl); // Try to submit without filling required fields const submitButton = page.locator('button[type="submit"], input[type="submit"], .submit-button').first(); await expect(submitButton).toBeVisible(); await submitButton.click(); // Assert - Verify validation errors appear const validationIndicators = [ page.locator('text=required'), page.locator('text=please fill'), page.locator('.error'), page.locator('.validation-error'), page.locator('[data-testid="validation-error"]'), page.locator('input:invalid') ]; let validationFound = false; for (const indicator of validationIndicators) { if (await indicator.count() > 0) { validationFound = true; break; } } expect(validationFound).toBe(true); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/learnwithcc/tally-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server