Rewrite step definitions with inline detection logic
- Replace abstraction functions with inline regex patterns in step definitions - Add clear test outcomes: Pass/Fail for testable features, Pending with specific prefixes (NOT IMPLEMENTED, CANNOT TEST, WRONG STEP, NOT ON THIS SCREEN) for non-testable features - Fix GherkinHighlighter to use step.text instead of step.originalLine for step definition matching - Update documentation with Test Outcomes section - Extend test:cucumber script to run all parsing steps Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,63 +14,91 @@ Given('le formulaire de création est vide', async function (this: FestipodWorld
|
||||
});
|
||||
|
||||
When('je remplis le champ {string} avec {string}', async function (this: FestipodWorld, fieldName: string, value: string) {
|
||||
const existing = this.formFields.get(fieldName);
|
||||
this.formFields.set(fieldName, {
|
||||
required: existing?.required ?? false,
|
||||
value
|
||||
});
|
||||
// Cannot fill form fields without browser automation
|
||||
this.attach(`CANNOT TEST: Filling field "${fieldName}" with "${value}" requires browser automation`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
When('je laisse le champ {string} vide', async function (this: FestipodWorld, fieldName: string) {
|
||||
const existing = this.formFields.get(fieldName);
|
||||
if (existing) {
|
||||
this.formFields.set(fieldName, { ...existing, value: '' });
|
||||
}
|
||||
// Cannot manipulate form fields without browser automation
|
||||
this.attach(`CANNOT TEST: Leaving field "${fieldName}" empty requires browser automation`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
When('je soumets le formulaire', async function (this: FestipodWorld) {
|
||||
this.attach('Form submitted', 'text/plain');
|
||||
// Cannot submit forms without browser automation
|
||||
this.attach('CANNOT TEST: Form submission requires browser automation', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('le formulaire contient le champ obligatoire {string}', async function (this: FestipodWorld, fieldName: string) {
|
||||
const field = this.formFields.get(fieldName);
|
||||
expect(field, `Field "${fieldName}" should exist`).to.not.be.undefined;
|
||||
expect(field?.required, `Field "${fieldName}" should be required`).to.equal(true);
|
||||
// This step is for form screens only (create-event)
|
||||
// For display screens, use different steps
|
||||
if (this.currentScreenId !== 'create-event') {
|
||||
this.attach(`WRONG STEP: "le formulaire contient le champ obligatoire" is for forms. Screen "${this.currentScreenId}" is not a form.`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
const source = this.getRenderedText();
|
||||
// CreateEventScreen.tsx: Required fields have " *" after label: >Label *<
|
||||
const escapedName = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`>${escapedName}\\s*\\*<`);
|
||||
expect(pattern.test(source), `Field "${fieldName}" should be marked as required (with *) in create-event screen`).to.be.true;
|
||||
});
|
||||
|
||||
Then('le formulaire contient les champs obligatoires suivants:', async function (this: FestipodWorld, dataTable) {
|
||||
// This step is for form screens only (create-event)
|
||||
// For display screens, use different steps
|
||||
if (this.currentScreenId !== 'create-event') {
|
||||
this.attach(`WRONG STEP: "le formulaire contient les champs obligatoires" is for forms. Screen "${this.currentScreenId}" is not a form.`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
const source = this.getRenderedText();
|
||||
const expectedFields = dataTable.raw().flat();
|
||||
expectedFields.forEach((fieldName: string) => {
|
||||
const field = this.formFields.get(fieldName);
|
||||
expect(field, `Field "${fieldName}" should exist`).to.not.be.undefined;
|
||||
expect(field?.required, `Field "${fieldName}" should be required`).to.equal(true);
|
||||
// CreateEventScreen.tsx: Required fields have " *" after label: >Label *<
|
||||
const escapedName = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`>${escapedName}\\s*\\*<`);
|
||||
expect(pattern.test(source), `Field "${fieldName}" should be marked as required (with *) in create-event screen`).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
Then('le champ {string} est facultatif', async function (this: FestipodWorld, fieldName: string) {
|
||||
const field = this.formFields.get(fieldName);
|
||||
if (field) {
|
||||
expect(field.required).to.equal(false);
|
||||
}
|
||||
const source = this.getRenderedText();
|
||||
// Optional fields have label without " *": >Label< followed by Input
|
||||
const escapedName = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// Check field exists but NOT marked as required
|
||||
const existsPattern = new RegExp(`>${escapedName}<`);
|
||||
const requiredPattern = new RegExp(`>${escapedName}\\s*\\*<`);
|
||||
expect(existsPattern.test(source), `Field "${fieldName}" should exist in screen`).to.be.true;
|
||||
expect(requiredPattern.test(source), `Field "${fieldName}" should NOT be marked as required`).to.be.false;
|
||||
});
|
||||
|
||||
Then('le champ {string} affiche {string}', async function (this: FestipodWorld, fieldName: string, expectedValue: string) {
|
||||
const field = this.formFields.get(fieldName);
|
||||
expect(field?.value).to.equal(expectedValue);
|
||||
// Cannot verify displayed field values without browser automation
|
||||
this.attach(`CANNOT TEST: Verifying field "${fieldName}" displays "${expectedValue}" requires browser automation`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('le champ {string} est présent', async function (this: FestipodWorld, fieldName: string) {
|
||||
const field = this.formFields.get(fieldName);
|
||||
expect(field, `Field "${fieldName}" should exist`).to.not.be.undefined;
|
||||
const source = this.getRenderedText();
|
||||
// Check that field label exists in screen source
|
||||
const escapedName = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`>${escapedName}[^<]*<`);
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`NOT FOUND: Field "${fieldName}" not present in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Then('une erreur de validation est affichée pour {string}', async function (this: FestipodWorld, fieldName: string) {
|
||||
const field = this.formFields.get(fieldName);
|
||||
expect(field?.required).to.equal(true);
|
||||
expect(field?.value).to.equal('');
|
||||
this.attach(`Validation error for: ${fieldName}`, 'text/plain');
|
||||
// Cannot verify validation errors without browser automation
|
||||
this.attach(`CANNOT TEST: Validation error for "${fieldName}" requires browser automation`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('le formulaire affiche {int} champs', async function (this: FestipodWorld, count: number) {
|
||||
expect(this.formFields.size).to.equal(count);
|
||||
// Cannot count form fields without specific analysis
|
||||
this.attach(`CANNOT TEST: Counting ${count} form fields requires more specific screen analysis`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
@@ -51,15 +51,39 @@ When('je navigue vers {string}', async function (this: FestipodWorld, pageName:
|
||||
});
|
||||
|
||||
When('je clique sur {string}', async function (this: FestipodWorld, elementName: string) {
|
||||
this.attach(`Clicked on: ${elementName}`, 'text/plain');
|
||||
const source = this.getRenderedText();
|
||||
// Check that a clickable element with this text exists (onClick handler + text content)
|
||||
const escapedName = elementName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`onClick[^>]*>[^<]*${escapedName}`, 'i');
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`MISSING: Clickable element "${elementName}" not found in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
When('je sélectionne {string}', async function (this: FestipodWorld, elementName: string) {
|
||||
this.attach(`Selected: ${elementName}`, 'text/plain');
|
||||
const source = this.getRenderedText();
|
||||
// Check that a selectable element with this text exists
|
||||
const escapedName = elementName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`onClick[^>]*>[^<]*${escapedName}`, 'i');
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`MISSING: Selectable element "${elementName}" not found in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
When('je clique sur le bouton {string}', async function (this: FestipodWorld, buttonName: string) {
|
||||
this.attach(`Clicked button: ${buttonName}`, 'text/plain');
|
||||
const source = this.getRenderedText();
|
||||
// Check that a Button component with this label exists
|
||||
const escapedName = buttonName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`<Button[^>]*>[^<]*${escapedName}[^<]*</Button>`, 'i');
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`MISSING: Button "${buttonName}" not found in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
When('je clique sur un participant', async function (this: FestipodWorld) {
|
||||
@@ -86,15 +110,42 @@ Then('je reste sur la page {string}', async function (this: FestipodWorld, pageN
|
||||
});
|
||||
|
||||
Then('l\'écran contient une section {string}', async function (this: FestipodWorld, sectionName: string) {
|
||||
expect(this.currentScreenId).to.not.be.null;
|
||||
this.attach(`Verified section: ${sectionName}`, 'text/plain');
|
||||
const found = this.hasText(sectionName);
|
||||
if (!found) {
|
||||
this.attach(`MISSING SECTION: "${sectionName}" not found in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Then('je peux annuler et revenir à l\'écran précédent', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('create-event');
|
||||
const source = this.getRenderedText();
|
||||
// Detect ✕ close button with onClick handler that calls navigate()
|
||||
const found = /onClick\s*=\s*\{\s*\(\)\s*=>\s*navigate\s*\(['"]home['"]\)\s*\}[^>]*>✕</.test(source);
|
||||
expect(found, 'Create event screen should have ✕ button with navigate("home")').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux naviguer vers {string}', async function (this: FestipodWorld, pageName: string) {
|
||||
const screenId = resolveScreenId(pageName);
|
||||
this.attach(`Navigation available to: ${screenId}`, 'text/plain');
|
||||
const source = this.getRenderedText();
|
||||
// Check that a navigation link to this screen exists: navigate('screenId') or onClick={() => navigate('screenId')}
|
||||
const pattern = new RegExp(`navigate\\s*\\(\\s*['"]${screenId}['"]\\s*\\)`);
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`MISSING: Navigation to "${screenId}" not found in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Then('la navigation affiche {string} comme actif', async function (this: FestipodWorld, menuItem: string) {
|
||||
this.attach(`Active menu: ${menuItem}`, 'text/plain');
|
||||
const source = this.getRenderedText();
|
||||
// Check that NavBar has an item with this label and active: true
|
||||
// Pattern: { icon: '...', label: 'menuItem', active: true }
|
||||
const escapedItem = menuItem.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`label:\\s*['"]${escapedItem}['"][^}]*active:\\s*true`, 'i');
|
||||
const found = pattern.test(source);
|
||||
if (!found) {
|
||||
this.attach(`MISSING: Menu item "${menuItem}" is not active in NavBar of screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,79 +1,119 @@
|
||||
import { Given, When, Then } from '@cucumber/cucumber';
|
||||
import { Given, Then } from '@cucumber/cucumber';
|
||||
import { expect } from 'chai';
|
||||
import type { FestipodWorld } from '../support/world';
|
||||
import { screenExpectedContent } from '../support/world';
|
||||
|
||||
Then('je peux voir la liste des participants', async function (this: FestipodWorld) {
|
||||
const screensWithParticipants = ['event-detail', 'participants-list', 'invite'];
|
||||
expect(screensWithParticipants, `Screen ${this.currentScreenId} should show participants`).to.include(this.currentScreenId);
|
||||
|
||||
// Verify the text "Participant" appears in the rendered content
|
||||
const hasParticipants = this.hasText('Participant') || this.hasText('participant') || this.hasText('inscrits');
|
||||
expect(hasParticipants, 'Page should display participants list').to.be.true;
|
||||
expect(this.currentScreenId).to.equal('event-detail');
|
||||
const source = this.getRenderedText();
|
||||
// EventDetailScreen.tsx has: <Avatar components and "Participants (12)" text
|
||||
const hasAvatars = /<Avatar/.test(source);
|
||||
const hasParticipantsSection = /Participants\s*\(\d+\)/.test(source);
|
||||
expect(hasAvatars, 'Event detail should have Avatar components for participants').to.be.true;
|
||||
expect(hasParticipantsSection, 'Event detail should have "Participants (N)" section').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux voir les détails de l\'événement', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('event-detail');
|
||||
// Verify event detail content is rendered
|
||||
const hasEventInfo = this.hasText('Description') || this.hasText('Participant') || this.hasText('inscrits');
|
||||
expect(hasEventInfo, 'Event detail page should show event information').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// EventDetailScreen.tsx has: <Title>, 📅, 🕓, 📍 emojis, and "À propos" section
|
||||
expect(/<Title[^>]*>[^<]+<\/Title>/.test(source), 'Event detail should have a Title').to.be.true;
|
||||
expect(/📅/.test(source), 'Event detail should have date emoji 📅').to.be.true;
|
||||
expect(/🕓/.test(source), 'Event detail should have time emoji 🕓').to.be.true;
|
||||
expect(/📍/.test(source), 'Event detail should have location emoji 📍').to.be.true;
|
||||
expect(/À propos/.test(source), 'Event detail should have "À propos" section').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux voir la section {string}', async function (this: FestipodWorld, sectionName: string) {
|
||||
const hasSection = this.hasText(sectionName);
|
||||
if (!hasSection) {
|
||||
const source = this.getRenderedText();
|
||||
// Detect section by text search
|
||||
const found = source.includes(sectionName);
|
||||
if (!found) {
|
||||
this.attach(`Looking for section: "${sectionName}"`, 'text/plain');
|
||||
this.attach(`Rendered text: ${this.getRenderedText().substring(0, 500)}...`, 'text/plain');
|
||||
this.attach(`Rendered text: ${source.substring(0, 500)}...`, 'text/plain');
|
||||
}
|
||||
expect(hasSection, `Section "${sectionName}" should be visible on screen`).to.be.true;
|
||||
expect(found, `Section "${sectionName}" should be visible on screen`).to.be.true;
|
||||
});
|
||||
|
||||
Then('la page affiche {int} éléments', async function (this: FestipodWorld, count: number) {
|
||||
// This is harder to verify without specific selectors, so we just log it
|
||||
this.attach(`Expected ${count} elements displayed`, 'text/plain');
|
||||
// Cannot count rendered elements without browser automation
|
||||
this.attach(`CANNOT TEST: Counting ${count} elements requires browser automation`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux voir mon profil', async function (this: FestipodWorld) {
|
||||
expect(['profile', 'user-profile']).to.include(this.currentScreenId);
|
||||
// Verify profile content
|
||||
const hasProfileContent = this.hasText('profil') || this.hasText('Profil');
|
||||
expect(hasProfileContent, 'Profile page should display profile content').to.be.true;
|
||||
expect(this.currentScreenId).to.equal('profile');
|
||||
const source = this.getRenderedText();
|
||||
// ProfileScreen.tsx has: <Avatar initials="MD" size="lg" />, <Title>Marie Dupont</Title>, @mariedupont
|
||||
expect(/<Avatar[^>]*initials="MD"[^>]*size="lg"/.test(source), 'Profile should have Avatar with initials="MD" and size="lg"').to.be.true;
|
||||
expect(/<Title[^>]*>Marie Dupont<\/Title>/.test(source), 'Profile should have Title "Marie Dupont"').to.be.true;
|
||||
expect(/@mariedupont/.test(source), 'Profile should have username @mariedupont').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux voir le profil de l\'utilisateur', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('user-profile');
|
||||
const hasProfileContent = this.hasText('Profil') || this.hasText('@');
|
||||
expect(hasProfileContent, 'User profile should display profile information').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// UserProfileScreen.tsx has: <Avatar initials="JD" size="lg" />, <Title>Jean Durand</Title>, @jeandurand
|
||||
expect(/<Avatar[^>]*initials="JD"[^>]*size="lg"/.test(source), 'User profile should have Avatar with initials="JD" and size="lg"').to.be.true;
|
||||
expect(/<Title[^>]*>Jean Durand<\/Title>/.test(source), 'User profile should have Title "Jean Durand"').to.be.true;
|
||||
expect(/@jeandurand/.test(source), 'User profile should have username @jeandurand').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux voir la liste des événements', async function (this: FestipodWorld) {
|
||||
expect(['events', 'home', 'profile']).to.include(this.currentScreenId);
|
||||
// Verify events list is shown
|
||||
const hasEvents = this.hasText('Événement') || this.hasText('événement') || this.hasText('inscrits');
|
||||
expect(hasEvents, 'Page should display events list').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
if (this.currentScreenId === 'home') {
|
||||
// HomeScreen.tsx has: "Événements à venir" text and EventCard components
|
||||
expect(/Événements à venir/.test(source), 'Home screen should have "Événements à venir" text').to.be.true;
|
||||
} else if (this.currentScreenId === 'events') {
|
||||
// EventsScreen.tsx has: EventCard components with event data
|
||||
expect(/<Card[^>]*onClick/.test(source), 'Events screen should have clickable Card components').to.be.true;
|
||||
} else {
|
||||
this.attach(`UNEXPECTED SCREEN: "${this.currentScreenId}" is not expected to show events list`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Then('je peux voir le QR code', async function (this: FestipodWorld) {
|
||||
expect(['profile', 'share-profile', 'meeting-points']).to.include(this.currentScreenId);
|
||||
// Check for QR code related content
|
||||
const hasQRContent = this.hasText('QR') || this.hasText('Partager') || this.hasText('partager');
|
||||
expect(hasQRContent, 'Page should have QR code or share functionality').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
if (this.currentScreenId === 'share-profile') {
|
||||
// ShareProfileScreen.tsx has: "QR Code" comment and "Scannez pour me retrouver" text
|
||||
expect(/QR Code/.test(source), 'Share profile should have "QR Code" text').to.be.true;
|
||||
expect(/Scannez pour me retrouver/.test(source), 'Share profile should have "Scannez pour me retrouver" text').to.be.true;
|
||||
} else if (this.currentScreenId === 'meeting-points') {
|
||||
// MeetingPointsScreen.tsx has: "Mon QR Code" text and "Scannez pour m'ajouter"
|
||||
expect(/Mon QR Code/.test(source), 'Meeting points should have "Mon QR Code" text').to.be.true;
|
||||
expect(/Scannez pour m'ajouter/.test(source), 'Meeting points should have "Scannez pour m\'ajouter" text').to.be.true;
|
||||
} else {
|
||||
// QR code is NOT on this screen
|
||||
this.attach(`NOT ON THIS SCREEN: QR code is on share-profile or meeting-points, not "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Then('je peux voir le lien de partage', async function (this: FestipodWorld) {
|
||||
expect(['profile', 'share-profile']).to.include(this.currentScreenId);
|
||||
const hasShareLink = this.hasText('Partager') || this.hasText('partager') || this.hasText('lien');
|
||||
expect(hasShareLink, 'Page should display share link functionality').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
if (this.currentScreenId === 'share-profile') {
|
||||
// ShareProfileScreen.tsx has: "Mon lien de profil" text and profileLink variable
|
||||
expect(/Mon lien de profil/.test(source), 'Share profile should have "Mon lien de profil" text').to.be.true;
|
||||
expect(/festipod\.app\/u\//.test(source), 'Share profile should have profile link URL').to.be.true;
|
||||
} else {
|
||||
// Share link is NOT on this screen
|
||||
this.attach(`NOT ON THIS SCREEN: Share link is on share-profile, not "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
});
|
||||
|
||||
Given('un événement existe avec les données:', async function (this: FestipodWorld, dataTable) {
|
||||
// Cannot set up test data without backend/database
|
||||
const eventData = dataTable.rowsHash();
|
||||
this.attach(`Event data: ${JSON.stringify(eventData)}`, 'text/plain');
|
||||
this.attach(`CANNOT TEST: Setting up event data requires backend: ${JSON.stringify(eventData)}`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Given('un utilisateur existe avec les données:', async function (this: FestipodWorld, dataTable) {
|
||||
// Cannot set up test data without backend/database
|
||||
const userData = dataTable.rowsHash();
|
||||
this.attach(`User data: ${JSON.stringify(userData)}`, 'text/plain');
|
||||
this.attach(`CANNOT TEST: Setting up user data requires backend: ${JSON.stringify(userData)}`, 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Given('je visualise l\'événement {string}', async function (this: FestipodWorld, eventName: string) {
|
||||
@@ -90,122 +130,119 @@ Given('je visualise le profil de {string}', async function (this: FestipodWorld,
|
||||
|
||||
Then('l\'écran affiche les informations de l\'événement', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('event-detail');
|
||||
// Verify actual content is rendered
|
||||
const expectedContent = screenExpectedContent['event-detail'] || [];
|
||||
const renderedText = this.getRenderedText();
|
||||
|
||||
let foundCount = 0;
|
||||
for (const content of expectedContent) {
|
||||
if (renderedText.includes(content)) {
|
||||
foundCount++;
|
||||
}
|
||||
}
|
||||
|
||||
expect(foundCount, `At least one expected content item should be present`).to.be.greaterThan(0);
|
||||
const source = this.getRenderedText();
|
||||
// EventDetailScreen.tsx has: <Title>, 📅, 🕓, 📍 emojis, and "À propos" section
|
||||
expect(/<Title[^>]*>[^<]+<\/Title>/.test(source), 'Event detail should have a Title').to.be.true;
|
||||
expect(/📅/.test(source), 'Event detail should have date emoji 📅').to.be.true;
|
||||
expect(/🕓/.test(source), 'Event detail should have time emoji 🕓').to.be.true;
|
||||
expect(/📍/.test(source), 'Event detail should have location emoji 📍').to.be.true;
|
||||
expect(/À propos/.test(source), 'Event detail should have "À propos" section').to.be.true;
|
||||
});
|
||||
|
||||
Then('l\'écran affiche les informations du profil', async function (this: FestipodWorld) {
|
||||
expect(['profile', 'user-profile']).to.include(this.currentScreenId);
|
||||
// Verify profile info is rendered
|
||||
const hasProfileInfo = this.hasText('Profil') || this.hasText('@') || this.hasText('Événement');
|
||||
expect(hasProfileInfo, 'Profile information should be displayed').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
if (this.currentScreenId === 'profile') {
|
||||
// ProfileScreen.tsx has: <Avatar initials="MD" size="lg" />, <Title>Marie Dupont</Title>, @mariedupont
|
||||
expect(/<Avatar[^>]*initials="MD"/.test(source), 'Profile should have Avatar with initials="MD"').to.be.true;
|
||||
expect(/<Title[^>]*>Marie Dupont<\/Title>/.test(source), 'Profile should have Title "Marie Dupont"').to.be.true;
|
||||
expect(/@mariedupont/.test(source), 'Profile should have username @mariedupont').to.be.true;
|
||||
} else if (this.currentScreenId === 'user-profile') {
|
||||
// UserProfileScreen.tsx has: <Avatar initials="JD" size="lg" />, <Title>Jean Durand</Title>, @jeandurand
|
||||
expect(/<Avatar[^>]*initials="JD"/.test(source), 'User profile should have Avatar with initials="JD"').to.be.true;
|
||||
expect(/<Title[^>]*>Jean Durand<\/Title>/.test(source), 'User profile should have Title "Jean Durand"').to.be.true;
|
||||
expect(/@jeandurand/.test(source), 'User profile should have username @jeandurand').to.be.true;
|
||||
} else {
|
||||
expect.fail(`Unexpected screen "${this.currentScreenId}" for profile info check`);
|
||||
}
|
||||
});
|
||||
|
||||
Then('je peux ajouter un commentaire', async function (this: FestipodWorld) {
|
||||
// Check for comment feature using precise detector
|
||||
const hasCommentFeature = this.hasField('Commentaire');
|
||||
|
||||
if (!hasCommentFeature) {
|
||||
this.attach(`MISSING FEATURE: Comment functionality is not implemented in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
this.attach(`Expected: textarea element or "commentaire" text in the screen`, 'text/plain');
|
||||
return 'pending'; // Mark as pending instead of failing
|
||||
}
|
||||
// EventDetailScreen.tsx does NOT have comment functionality (no textarea, no "commentaire" text)
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Comment functionality not in EventDetailScreen.tsx', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux ajouter une note', async function (this: FestipodWorld) {
|
||||
// Check for note feature - similar to comment
|
||||
const hasNoteFeature = this.hasText('Note') || this.hasText('note') || this.hasElement('textarea');
|
||||
|
||||
if (!hasNoteFeature) {
|
||||
this.attach(`MISSING FEATURE: Note functionality is not implemented in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
// No screen has note functionality implemented
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Note functionality not implemented in any screen', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux filtrer les événements par période', async function (this: FestipodWorld) {
|
||||
// Check for period filter feature
|
||||
const hasPeriodFilter = this.hasText('mois') || this.hasText('trimestre') || this.hasText('année') ||
|
||||
this.hasText('période') || this.hasText('Période');
|
||||
|
||||
if (!hasPeriodFilter) {
|
||||
this.attach(`MISSING FEATURE: Period filter is not implemented in screen "${this.currentScreenId}"`, 'text/plain');
|
||||
return 'pending';
|
||||
}
|
||||
// EventsScreen.tsx has filter badges (Tous, Cette semaine, Proches, Amis) but NOT period filter (mois/trimestre/année)
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Period filter (mois/trimestre/année) not in EventsScreen.tsx', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux modifier un commentaire', async function (this: FestipodWorld) {
|
||||
// Comment editing is typically available where adding is
|
||||
const hasEditFeature = this.hasText('Modifier') || this.hasText('modifier') || this.hasElement('button');
|
||||
expect(hasEditFeature, 'Edit functionality should be available').to.be.true;
|
||||
// No comment edit functionality exists in any screen
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Comment edit functionality not implemented', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux supprimer un commentaire', async function (this: FestipodWorld) {
|
||||
// Delete is typically available where edit is
|
||||
const hasDeleteFeature = this.hasText('Supprimer') || this.hasText('supprimer') || this.hasElement('button');
|
||||
expect(hasDeleteFeature, 'Delete functionality should be available').to.be.true;
|
||||
// No comment delete functionality exists in any screen
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Comment delete functionality not implemented', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux m\'inscrire à l\'événement', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('event-detail');
|
||||
// Check for registration button
|
||||
const hasRegisterFeature = this.hasText('inscription') || this.hasText('Participer') ||
|
||||
this.hasText('participer') || this.hasText('S\'inscrire') ||
|
||||
this.hasText('Rejoindre');
|
||||
expect(hasRegisterFeature, 'Registration feature should be available').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// EventDetailScreen.tsx line 49: {isJoined ? '✓ Inscrit' : 'Participer'}
|
||||
// The button shows "Participer" when not joined
|
||||
const hasParticiperButton = /isJoined \? '✓ Inscrit' : 'Participer'/.test(source);
|
||||
expect(hasParticiperButton, 'Event detail should have Participer/Inscrit toggle button').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux me désinscrire de l\'événement', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('event-detail');
|
||||
// Unregister is typically on the same page as register
|
||||
const hasUnregisterFeature = this.hasText('désinscri') || this.hasText('Annuler') ||
|
||||
this.hasText('Quitter') || this.hasElement('button');
|
||||
expect(hasUnregisterFeature, 'Unregister feature should be available').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// EventDetailScreen.tsx line 49: {isJoined ? '✓ Inscrit' : 'Participer'}
|
||||
// Same button toggles - clicking "✓ Inscrit" will unregister
|
||||
const hasInscritButton = /isJoined \? '✓ Inscrit' : 'Participer'/.test(source);
|
||||
expect(hasInscritButton, 'Event detail should have Participer/Inscrit toggle button (click to unregister)').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux contacter l\'utilisateur', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('user-profile');
|
||||
// Check for contact functionality
|
||||
const hasContactFeature = this.hasText('Contact') || this.hasText('Message') ||
|
||||
this.hasText('message') || this.hasElement('button');
|
||||
expect(hasContactFeature, 'Contact feature should be available').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// UserProfileScreen.tsx line 44: <Button>Contacter</Button>
|
||||
const hasContactButton = /<Button>Contacter<\/Button>/.test(source);
|
||||
expect(hasContactButton, 'User profile should have "Contacter" button').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux voir les événements auxquels l\'utilisateur a participé', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('user-profile');
|
||||
// Check for user's events
|
||||
const hasUserEvents = this.hasText('Événement') || this.hasText('événement') ||
|
||||
this.hasText('Participation') || this.hasText('participation');
|
||||
expect(hasUserEvents, 'User events should be visible').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// UserProfileScreen.tsx line 52: "Événements en commun" section with pastEvents
|
||||
expect(/Événements en commun/.test(source), 'User profile should have "Événements en commun" section').to.be.true;
|
||||
expect(/pastEvents/.test(source), 'User profile should display pastEvents data').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux configurer mes notifications', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('settings');
|
||||
// Check for notification settings
|
||||
const hasNotificationSetting = this.hasText('Notification') || this.hasText('notification');
|
||||
expect(hasNotificationSetting, 'Notification settings should be visible').to.be.true;
|
||||
const source = this.getRenderedText();
|
||||
// SettingsScreen.tsx line 25: <Text>Notifications</Text> with Toggle
|
||||
expect(/>Notifications</.test(source), 'Settings should have "Notifications" text').to.be.true;
|
||||
expect(/<Toggle[^>]*checked=\{notifications\}/.test(source), 'Settings should have Toggle for notifications').to.be.true;
|
||||
});
|
||||
|
||||
Then('je peux définir mon rayon de notification', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('settings');
|
||||
// Check for location/radius setting
|
||||
const hasRadiusSetting = this.hasText('Localisation') || this.hasText('localisation') ||
|
||||
this.hasText('rayon') || this.hasText('Rayon');
|
||||
expect(hasRadiusSetting, 'Location/radius setting should be visible').to.be.true;
|
||||
// SettingsScreen.tsx has "Localisation" toggle but NOT "rayon" or "km" setting
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Radius setting (rayon/km) is not in SettingsScreen.tsx', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Then('je peux définir mes thématiques d\'intérêt', async function (this: FestipodWorld) {
|
||||
expect(this.currentScreenId).to.equal('settings');
|
||||
// Settings page should allow configuring interests (or it could be on profile)
|
||||
// For now just verify we're on settings
|
||||
expect(this.currentScreen, 'Settings screen should be loaded').to.not.be.null;
|
||||
// SettingsScreen.tsx does NOT have thematic/interest settings
|
||||
// This feature is NOT implemented in the UI
|
||||
this.attach('NOT IMPLEMENTED: Thematic/interest settings not in SettingsScreen.tsx', 'text/plain');
|
||||
return 'pending';
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user