mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 13:23:05 +08:00
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Zack Pollard <zackpollard@Zacks-MBP.local>
632 lines
24 KiB
TypeScript
632 lines
24 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test';
|
|
import { apiPost, apiPut, API, ensureAuth, getAuthHeaders, parseCookie } from './helpers';
|
|
|
|
interface SetupResult {
|
|
surveyId: string;
|
|
slug: string;
|
|
questionIds: Record<string, string>;
|
|
}
|
|
|
|
let cookie: string;
|
|
|
|
test.beforeAll(async () => {
|
|
cookie = await ensureAuth();
|
|
});
|
|
|
|
test.beforeEach(async ({ context }) => {
|
|
const BASE = process.env.BASE_URL || 'http://localhost:5173';
|
|
const { name, value } = parseCookie(cookie);
|
|
await context.addCookies([{ name, value, url: BASE }]);
|
|
});
|
|
|
|
async function createFullSurvey(): Promise<SetupResult> {
|
|
const survey = await apiPost('/api/surveys', { title: 'E2E All 10 Types' });
|
|
const surveyId = survey.id;
|
|
|
|
const section1 = await apiPost(`/api/surveys/${surveyId}/sections`, { title: 'Choice Questions' });
|
|
const section2 = await apiPost(`/api/surveys/${surveyId}/sections`, { title: 'Input Questions' });
|
|
const section3 = await apiPost(`/api/surveys/${surveyId}/sections`, { title: 'Scale Questions' });
|
|
|
|
const questions = [
|
|
{
|
|
key: 'radio',
|
|
sectionId: section1.id,
|
|
body: {
|
|
text: 'Pick a color',
|
|
type: 'radio',
|
|
options: [
|
|
{ label: 'Red', value: 'Red' },
|
|
{ label: 'Blue', value: 'Blue' },
|
|
],
|
|
required: true,
|
|
},
|
|
},
|
|
{
|
|
key: 'checkbox',
|
|
sectionId: section1.id,
|
|
body: {
|
|
text: 'Select hobbies',
|
|
type: 'checkbox',
|
|
options: [
|
|
{ label: 'Reading', value: 'Reading' },
|
|
{ label: 'Gaming', value: 'Gaming' },
|
|
],
|
|
required: true,
|
|
},
|
|
},
|
|
{
|
|
key: 'dropdown',
|
|
sectionId: section1.id,
|
|
body: {
|
|
text: 'Choose a country',
|
|
type: 'dropdown',
|
|
options: [
|
|
{ label: 'USA', value: 'USA' },
|
|
{ label: 'UK', value: 'UK' },
|
|
{ label: 'Germany', value: 'Germany' },
|
|
],
|
|
required: true,
|
|
},
|
|
},
|
|
|
|
{
|
|
key: 'text',
|
|
sectionId: section2.id,
|
|
body: { text: 'Your name', type: 'text', required: true, placeholder: 'Enter name' },
|
|
},
|
|
{
|
|
key: 'number',
|
|
sectionId: section2.id,
|
|
body: { text: 'Your age', type: 'number', required: true, config: { min: 0, max: 120 } },
|
|
},
|
|
{
|
|
key: 'email',
|
|
sectionId: section2.id,
|
|
body: { text: 'Your email', type: 'email', required: true, placeholder: 'you@example.com' },
|
|
},
|
|
{ key: 'textarea', sectionId: section2.id, body: { text: 'Tell us more', type: 'textarea', required: false } },
|
|
|
|
{
|
|
key: 'rating',
|
|
sectionId: section3.id,
|
|
body: { text: 'Rate our service', type: 'rating', required: true, config: { scaleMax: 5 } },
|
|
},
|
|
{ key: 'nps', sectionId: section3.id, body: { text: 'How likely to recommend?', type: 'nps', required: true } },
|
|
{ key: 'likert', sectionId: section3.id, body: { text: 'I am satisfied', type: 'likert', required: true } },
|
|
];
|
|
|
|
const questionIds: Record<string, string> = {};
|
|
for (const q of questions) {
|
|
const created = await apiPost(`/api/surveys/${surveyId}/sections/${q.sectionId}/questions`, q.body);
|
|
questionIds[q.key] = created.id;
|
|
}
|
|
|
|
const slug = `e2e-full-${Date.now()}`;
|
|
await apiPut(`/api/surveys/${surveyId}`, { slug });
|
|
await apiPut(`/api/surveys/${surveyId}/publish`);
|
|
|
|
return { surveyId, slug, questionIds };
|
|
}
|
|
|
|
async function waitForTransition(page: Page) {
|
|
await page.waitForTimeout(400);
|
|
}
|
|
|
|
async function clickNext(page: Page, label = 'Next') {
|
|
await waitForTransition(page);
|
|
await page.getByRole('button', { name: label }).first().click();
|
|
}
|
|
|
|
async function dismissSectionHeader(page: Page) {
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
}
|
|
|
|
test.describe('Full survey with all 10 question types', () => {
|
|
let setup: SetupResult;
|
|
|
|
test.beforeAll(async () => {
|
|
setup = await createFullSurvey();
|
|
});
|
|
|
|
test('completes survey with all question types', async ({ page }) => {
|
|
await page.goto(`/s/${setup.slug}`);
|
|
await page.getByRole('button', { name: 'Get Started' }).click();
|
|
|
|
await dismissSectionHeader(page);
|
|
|
|
await expect(page.getByText('Pick a color')).toBeVisible({ timeout: 3000 });
|
|
await page.getByRole('radio', { name: 'Blue' }).click();
|
|
// Auto-advances
|
|
await expect(page.getByText('Select hobbies')).toBeVisible({ timeout: 3000 });
|
|
|
|
await waitForTransition(page);
|
|
await page.getByRole('checkbox', { name: 'Reading' }).click();
|
|
await page.getByRole('checkbox', { name: 'Gaming' }).click();
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('Choose a country')).toBeVisible({ timeout: 3000 });
|
|
await waitForTransition(page);
|
|
await page.locator('select').selectOption('Germany');
|
|
await clickNext(page);
|
|
|
|
await dismissSectionHeader(page);
|
|
|
|
await expect(page.getByText('Your name')).toBeVisible({ timeout: 3000 });
|
|
await page.getByPlaceholder('Enter name').fill('E2E Tester');
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('Your age')).toBeVisible({ timeout: 3000 });
|
|
await waitForTransition(page);
|
|
await page.locator('input[type="number"]').fill('30');
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('Your email')).toBeVisible({ timeout: 3000 });
|
|
await page.getByPlaceholder('you@example.com').fill('e2e@test.com');
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('Tell us more')).toBeVisible({ timeout: 3000 });
|
|
await clickNext(page, 'Skip');
|
|
|
|
await dismissSectionHeader(page);
|
|
|
|
await expect(page.getByText('Rate our service')).toBeVisible({ timeout: 3000 });
|
|
await waitForTransition(page);
|
|
await page.locator('[data-rating-value="4"]').click();
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('How likely to recommend?')).toBeVisible({ timeout: 3000 });
|
|
await waitForTransition(page);
|
|
await page.getByLabel('Score 9').click();
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('I am satisfied')).toBeVisible({ timeout: 3000 });
|
|
await waitForTransition(page);
|
|
await page.getByRole('button', { name: 'Agree', exact: true }).click();
|
|
await clickNext(page, 'Submit');
|
|
|
|
await expect(page.getByText('Thank you!')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('results contain all question types', async () => {
|
|
const resumeRes = await fetch(`${API}/api/s/${setup.slug}/resume`);
|
|
const cookies = resumeRes.headers.get('set-cookie') ?? '';
|
|
const ridMatch = cookies.match(/rid_[^=]+=([^;]+)/);
|
|
const rid = ridMatch?.[1];
|
|
|
|
if (rid) {
|
|
const answers = [
|
|
{ questionId: setup.questionIds.radio, value: 'Red' },
|
|
{ questionId: setup.questionIds.checkbox, value: 'Reading' },
|
|
{ questionId: setup.questionIds.dropdown, value: 'USA' },
|
|
{ questionId: setup.questionIds.text, value: 'API User' },
|
|
{ questionId: setup.questionIds.number, value: '25' },
|
|
{ questionId: setup.questionIds.email, value: 'api@test.com' },
|
|
{ questionId: setup.questionIds.rating, value: '5' },
|
|
{ questionId: setup.questionIds.nps, value: '10' },
|
|
{ questionId: setup.questionIds.likert, value: 'Strongly Agree' },
|
|
];
|
|
await fetch(`${API}/api/s/${setup.slug}/answers/batch`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: `rid_${setup.slug}=${rid}` },
|
|
body: JSON.stringify({ answers }),
|
|
});
|
|
await fetch(`${API}/api/s/${setup.slug}/complete`, {
|
|
method: 'POST',
|
|
headers: { Cookie: `rid_${setup.slug}=${rid}` },
|
|
});
|
|
}
|
|
|
|
const res = await fetch(`${API}/api/surveys/${setup.surveyId}/results`, {
|
|
headers: getAuthHeaders(),
|
|
});
|
|
const data = await res.json();
|
|
expect(data.respondentCounts.completed).toBeGreaterThanOrEqual(1);
|
|
expect(data.results.length).toBeGreaterThanOrEqual(5);
|
|
});
|
|
|
|
test('CSV export works', async () => {
|
|
const res = await fetch(`${API}/api/surveys/${setup.surveyId}/results/export?format=csv`, {
|
|
headers: getAuthHeaders(),
|
|
});
|
|
expect(res.ok).toBe(true);
|
|
expect(res.headers.get('content-type')).toContain('text/csv');
|
|
const csv = await res.text();
|
|
expect(csv).toContain('respondent_id');
|
|
expect(csv.split('\n').length).toBeGreaterThan(1);
|
|
});
|
|
|
|
test('JSON export works', async () => {
|
|
const res = await fetch(`${API}/api/surveys/${setup.surveyId}/results/export?format=json`, {
|
|
headers: getAuthHeaders(),
|
|
});
|
|
expect(res.ok).toBe(true);
|
|
const data = await res.json();
|
|
expect(Array.isArray(data)).toBe(true);
|
|
expect(data.length).toBeGreaterThanOrEqual(1);
|
|
expect(data[0]).toHaveProperty('respondentId');
|
|
expect(data[0]).toHaveProperty('answers');
|
|
});
|
|
|
|
test('results page renders with stats and question results', async ({ page }) => {
|
|
await page.goto(`/results/${setup.surveyId}`);
|
|
|
|
await expect(page.getByText('Total')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.getByText('Completed')).toBeVisible();
|
|
await expect(page.getByText('Completion')).toBeVisible();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Pick a color' })).toBeVisible({ timeout: 10000 });
|
|
|
|
await expect(page.getByRole('button', { name: 'CSV' })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: 'JSON' })).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Survey duplication', () => {
|
|
test('duplicates a survey via API', async () => {
|
|
const survey = await apiPost('/api/surveys', { title: 'Original Survey' });
|
|
const section = await apiPost(`/api/surveys/${survey.id}/sections`, { title: 'Section 1' });
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Q1',
|
|
type: 'radio',
|
|
options: [
|
|
{ label: 'A', value: 'A' },
|
|
{ label: 'B', value: 'B' },
|
|
],
|
|
});
|
|
|
|
const result = await apiPost(`/api/surveys/${survey.id}/duplicate`);
|
|
const dup = result.survey ?? result;
|
|
expect(dup.title).toBe('Original Survey (Copy)');
|
|
expect(dup.status).toBe('draft');
|
|
expect(dup.id).not.toBe(survey.id);
|
|
|
|
// Retry: the duplicated survey's DO initialises asynchronously.
|
|
let data: Record<string, unknown[]> = { sections: [], questions: [] };
|
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
const res = await fetch(`${API}/api/surveys/${dup.id}`, {
|
|
headers: getAuthHeaders(),
|
|
});
|
|
data = await res.json();
|
|
if (data.sections?.length > 0) break;
|
|
await new Promise((r) => setTimeout(r, 1000));
|
|
}
|
|
expect(data.sections.length).toBe(1);
|
|
expect(data.questions.length).toBe(1);
|
|
});
|
|
|
|
test('duplicate button works on dashboard', async ({ page }) => {
|
|
await page.goto('/');
|
|
await expect(page.getByRole('heading', { name: 'Surveys' })).toBeVisible();
|
|
|
|
const initialCards = await page.locator('[class*="card-hover"]').count();
|
|
|
|
const duplicateBtn = page.locator('button[title="Duplicate"]').first();
|
|
if (await duplicateBtn.isVisible()) {
|
|
await duplicateBtn.click();
|
|
await expect(page.locator('[class*="card-hover"]')).toHaveCount(initialCards + 1, { timeout: 5000 });
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Embed page', () => {
|
|
let slug: string;
|
|
|
|
test.beforeAll(async () => {
|
|
const survey = await apiPost('/api/surveys', { title: 'Embed Test' });
|
|
const section = await apiPost(`/api/surveys/${survey.id}/sections`, { title: 'S1' });
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Quick Q',
|
|
type: 'text',
|
|
required: true,
|
|
});
|
|
slug = `embed-test-${Date.now()}`;
|
|
await apiPut(`/api/surveys/${survey.id}`, { slug });
|
|
await apiPut(`/api/surveys/${survey.id}/publish`);
|
|
});
|
|
|
|
test('embed page loads the survey', async ({ page }) => {
|
|
await page.goto(`/embed/${slug}`);
|
|
await expect(page.getByText('Embed Test')).toBeVisible({ timeout: 5000 });
|
|
await page.getByRole('button', { name: 'Get Started' }).click();
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByText('Quick Q')).toBeVisible({ timeout: 3000 });
|
|
});
|
|
});
|
|
|
|
test.describe('Survey with skip logic across question types', () => {
|
|
let setup: { surveyId: string; slug: string };
|
|
|
|
test.beforeAll(async () => {
|
|
const survey = await apiPost('/api/surveys', { title: 'Skip Logic Flow Test' });
|
|
const section = await apiPost(`/api/surveys/${survey.id}/sections`, { title: 'Feedback' });
|
|
|
|
const q1 = await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Have you used our product?',
|
|
type: 'radio',
|
|
required: true,
|
|
options: [
|
|
{ label: 'Yes', value: 'Yes' },
|
|
{ label: 'No', value: 'No' },
|
|
],
|
|
});
|
|
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Rate your experience',
|
|
type: 'rating',
|
|
required: true,
|
|
config: { scaleMax: 5 },
|
|
conditional: { showIf: { questionId: q1.id, condition: 'equals', value: 'Yes' } },
|
|
});
|
|
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'What prevented you from trying it?',
|
|
type: 'text',
|
|
required: true,
|
|
placeholder: 'Tell us why',
|
|
conditional: { showIf: { questionId: q1.id, condition: 'equals', value: 'No' } },
|
|
});
|
|
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'How likely to recommend?',
|
|
type: 'nps',
|
|
required: true,
|
|
});
|
|
|
|
const slug = `e2e-skip-flow-${Date.now()}`;
|
|
await apiPut(`/api/surveys/${survey.id}`, { slug });
|
|
await apiPut(`/api/surveys/${survey.id}/publish`);
|
|
setup = { surveyId: survey.id, slug };
|
|
});
|
|
|
|
test('skip logic works: Yes path shows rating, skips text', async ({ page }) => {
|
|
await page.goto(`/s/${setup.slug}`);
|
|
await page.getByRole('button', { name: 'Get Started' }).click();
|
|
await dismissSectionHeader(page);
|
|
|
|
await expect(page.getByText('Have you used our product?')).toBeVisible({ timeout: 3000 });
|
|
await page.getByRole('radio', { name: 'Yes' }).click();
|
|
|
|
await expect(page.getByText('Rate your experience')).toBeVisible({ timeout: 3000 });
|
|
await expect(page.getByText('What prevented you from trying it?')).not.toBeVisible();
|
|
|
|
await page.locator('[data-rating-value="4"]').click();
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('How likely to recommend?')).toBeVisible({ timeout: 3000 });
|
|
await page.getByLabel('Score 8').click();
|
|
await clickNext(page, 'Submit');
|
|
|
|
await expect(page.getByText('Thank you!')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('skip logic works: No path shows text, skips rating', async ({ page }) => {
|
|
await page.goto(`/s/${setup.slug}`);
|
|
await page.getByRole('button', { name: 'Get Started' }).click();
|
|
await dismissSectionHeader(page);
|
|
|
|
await expect(page.getByText('Have you used our product?')).toBeVisible({ timeout: 3000 });
|
|
await page.getByRole('radio', { name: 'No' }).click();
|
|
|
|
await expect(page.getByText('What prevented you from trying it?')).toBeVisible({ timeout: 3000 });
|
|
await expect(page.getByText('Rate your experience')).not.toBeVisible();
|
|
|
|
await page.getByPlaceholder('Tell us why').fill('No time');
|
|
await clickNext(page);
|
|
|
|
await expect(page.getByText('How likely to recommend?')).toBeVisible({ timeout: 3000 });
|
|
await page.getByLabel('Score 5').click();
|
|
await clickNext(page, 'Submit');
|
|
|
|
await expect(page.getByText('Thank you!')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('skipped questions are not in results', async () => {
|
|
// Create a fresh survey to avoid interference from browser tests
|
|
const survey = await apiPost('/api/surveys', { title: 'Skip Results Test' });
|
|
const section = await apiPost(`/api/surveys/${survey.id}/sections`, { title: 'S1' });
|
|
const q1 = await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Used product?',
|
|
type: 'radio',
|
|
required: true,
|
|
options: [
|
|
{ label: 'Yes', value: 'Yes' },
|
|
{ label: 'No', value: 'No' },
|
|
],
|
|
});
|
|
const qRating = await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Rate it',
|
|
type: 'rating',
|
|
required: true,
|
|
config: { scaleMax: 5 },
|
|
conditional: { showIf: { questionId: q1.id, condition: 'equals', value: 'Yes' } },
|
|
});
|
|
const qText = await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Why not?',
|
|
type: 'text',
|
|
required: true,
|
|
conditional: { showIf: { questionId: q1.id, condition: 'equals', value: 'No' } },
|
|
});
|
|
const qNps = await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Recommend?',
|
|
type: 'nps',
|
|
required: true,
|
|
});
|
|
const slug = `e2e-skip-results-${Date.now()}`;
|
|
await apiPut(`/api/surveys/${survey.id}`, { slug });
|
|
await apiPut(`/api/surveys/${survey.id}/publish`);
|
|
|
|
async function submitResponse(answers: Array<{ questionId: string; value: string }>) {
|
|
const resumeRes = await fetch(`${API}/api/s/${slug}/resume`);
|
|
const cookies = resumeRes.headers.get('set-cookie') ?? '';
|
|
const ridMatch = cookies.match(/rid_[^=]+=([^;]+)/);
|
|
const rid = ridMatch?.[1];
|
|
expect(rid).toBeTruthy();
|
|
const cookie = `rid_${slug}=${rid}`;
|
|
await fetch(`${API}/api/s/${slug}/answers/batch`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Cookie: cookie },
|
|
body: JSON.stringify({ answers }),
|
|
});
|
|
await fetch(`${API}/api/s/${slug}/complete`, {
|
|
method: 'POST',
|
|
headers: { Cookie: cookie },
|
|
});
|
|
}
|
|
|
|
await submitResponse([
|
|
{ questionId: q1.id, value: 'Yes' },
|
|
{ questionId: qRating.id, value: '4' },
|
|
{ questionId: qNps.id, value: '8' },
|
|
]);
|
|
await submitResponse([
|
|
{ questionId: q1.id, value: 'No' },
|
|
{ questionId: qText.id, value: 'No time' },
|
|
{ questionId: qNps.id, value: '5' },
|
|
]);
|
|
|
|
const res = await fetch(`${API}/api/surveys/${survey.id}/results`, {
|
|
headers: getAuthHeaders(),
|
|
});
|
|
const data = await res.json();
|
|
expect(data.respondentCounts.completed).toBe(2);
|
|
|
|
// Rating question should have 1 response (only the "Yes" path respondent)
|
|
const ratingResult = data.results.find((r: { questionId: string }) => r.questionId === qRating.id);
|
|
if (ratingResult) {
|
|
const totalRatingResponses = ratingResult.answers.reduce((sum: number, a: { count: number }) => sum + a.count, 0);
|
|
expect(totalRatingResponses).toBe(1);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Enter key advances on single-line inputs', () => {
|
|
let slug: string;
|
|
|
|
test.beforeAll(async () => {
|
|
const survey = await apiPost('/api/surveys', { title: 'Enter Key Test' });
|
|
const section = await apiPost(`/api/surveys/${survey.id}/sections`, { title: 'Inputs' });
|
|
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Your name',
|
|
type: 'text',
|
|
required: true,
|
|
placeholder: 'Your name',
|
|
});
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Your email',
|
|
type: 'email',
|
|
required: true,
|
|
placeholder: 'you@example.com',
|
|
});
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Your age',
|
|
type: 'number',
|
|
required: true,
|
|
});
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Any comments',
|
|
type: 'textarea',
|
|
required: false,
|
|
placeholder: 'Tell us more',
|
|
});
|
|
await apiPost(`/api/surveys/${survey.id}/sections/${section.id}/questions`, {
|
|
text: 'Rate us',
|
|
type: 'rating',
|
|
required: true,
|
|
config: { scaleMax: 5 },
|
|
});
|
|
|
|
slug = `enter-key-${Date.now()}`;
|
|
await apiPut(`/api/surveys/${survey.id}`, { slug });
|
|
await apiPut(`/api/surveys/${survey.id}/publish`);
|
|
});
|
|
|
|
async function startSurveyAtFirstQuestion(page: Page) {
|
|
await page.goto(`/s/${slug}`);
|
|
await page.getByRole('button', { name: 'Get Started' }).click();
|
|
await dismissSectionHeader(page);
|
|
await expect(page.getByRole('heading', { name: 'Your name' })).toBeVisible({ timeout: 5000 });
|
|
}
|
|
|
|
test('Enter on text input advances to the next question', async ({ page }) => {
|
|
await startSurveyAtFirstQuestion(page);
|
|
|
|
const nameInput = page.getByPlaceholder('Your name');
|
|
await nameInput.fill('Alice');
|
|
await nameInput.press('Enter');
|
|
|
|
await expect(page.getByRole('heading', { name: 'Your email' })).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('Enter on email and number inputs advances', async ({ page }) => {
|
|
await startSurveyAtFirstQuestion(page);
|
|
|
|
await page.getByPlaceholder('Your name').fill('Bob');
|
|
await page.getByPlaceholder('Your name').press('Enter');
|
|
await expect(page.getByRole('heading', { name: 'Your email' })).toBeVisible({ timeout: 5000 });
|
|
await waitForTransition(page);
|
|
|
|
await page.getByPlaceholder('you@example.com').fill('bob@test.com');
|
|
await page.getByPlaceholder('you@example.com').press('Enter');
|
|
await expect(page.getByRole('heading', { name: 'Your age' })).toBeVisible({ timeout: 5000 });
|
|
await waitForTransition(page);
|
|
|
|
await page.locator('input[type="number"]').fill('30');
|
|
await page.locator('input[type="number"]').press('Enter');
|
|
await expect(page.getByRole('heading', { name: 'Any comments' })).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('Enter in textarea inserts a newline instead of advancing', async ({ page }) => {
|
|
await startSurveyAtFirstQuestion(page);
|
|
|
|
// Race through to the textarea question via the regular Next button so we
|
|
// don't depend on the feature under test.
|
|
await page.getByPlaceholder('Your name').fill('Carol');
|
|
await clickNext(page);
|
|
await expect(page.getByRole('heading', { name: 'Your email' })).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.getByPlaceholder('you@example.com').fill('carol@test.com');
|
|
await clickNext(page);
|
|
await expect(page.getByRole('heading', { name: 'Your age' })).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.locator('input[type="number"]').fill('25');
|
|
await clickNext(page);
|
|
await expect(page.getByRole('heading', { name: 'Any comments' })).toBeVisible({ timeout: 5000 });
|
|
|
|
const textarea = page.locator('textarea');
|
|
await expect(textarea).toBeVisible({ timeout: 5000 });
|
|
await textarea.fill('line one');
|
|
await textarea.press('Enter');
|
|
await textarea.pressSequentially('line two');
|
|
|
|
// Enter must not have advanced us
|
|
await expect(page.getByRole('heading', { name: 'Any comments' })).toBeVisible();
|
|
const value = await textarea.inputValue();
|
|
expect(value).toBe('line one\nline two');
|
|
});
|
|
});
|
|
|
|
test.describe('Survey builder with new types', () => {
|
|
test('can add all question types in the builder', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.getByRole('link', { name: 'New Survey' }).first().click();
|
|
await page.getByRole('button', { name: 'Blank Survey' }).click();
|
|
await page.getByPlaceholder('Survey title...').fill('Builder Types Test');
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
await expect(page.getByText('Edit Survey')).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.getByText('Add section').click();
|
|
await page.getByPlaceholder('e.g., About You').fill('All Types');
|
|
|
|
const typesToAdd = ['Rating', 'NPS', 'Number', 'Dropdown', 'Likert'];
|
|
for (const type of typesToAdd) {
|
|
// The add-question chips are the last buttons with these names on the page
|
|
await page.getByRole('button', { name: type }).last().click();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
|
|
// The new section starts empty, so exactly 5 questions are expected.
|
|
await expect(page.getByText('5 questions', { exact: true })).toBeVisible({ timeout: 3000 });
|
|
});
|
|
});
|