first commit

This commit is contained in:
Sylvain Duchesne
2026-01-18 11:53:42 +01:00
commit f04f15d926
112 changed files with 24858 additions and 0 deletions
+423
View File
@@ -0,0 +1,423 @@
// Auto-generated by scripts/extract-step-definitions.ts
// Do not edit manually - run "bun run steps:extract" to regenerate
export interface StepDefinitionInfo {
pattern: string;
keyword: 'Given' | 'When' | 'Then';
file: string;
sourceCode: string;
lineNumber: number;
}
export const stepDefinitions: StepDefinitionInfo[] = [
{
"pattern": "je suis sur la page {string}",
"keyword": "Given",
"file": "navigation.steps.ts",
"sourceCode": "Given('je suis sur la page {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n this.navigateTo(`#/demo/${screenId}`);\n});",
"lineNumber": 31
},
{
"pattern": "je suis connecté en tant qu\\",
"keyword": "Given",
"file": "navigation.steps.ts",
"sourceCode": "Given('je suis connecté en tant qu\\'utilisateur', async function (this: FestipodWorld) {\n this.isAuthenticated = true;\n});",
"lineNumber": 36
},
{
"pattern": "je suis connecté",
"keyword": "Given",
"file": "navigation.steps.ts",
"sourceCode": "Given('je suis connecté', async function (this: FestipodWorld) {\n this.isAuthenticated = true;\n});",
"lineNumber": 40
},
{
"pattern": "je ne suis pas connecté",
"keyword": "Given",
"file": "navigation.steps.ts",
"sourceCode": "Given('je ne suis pas connecté', async function (this: FestipodWorld) {\n this.isAuthenticated = false;\n});",
"lineNumber": 44
},
{
"pattern": "je navigue vers {string}",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je navigue vers {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n this.navigateTo(`#/demo/${screenId}`);\n});",
"lineNumber": 48
},
{
"pattern": "je clique sur {string}",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je clique sur {string}', async function (this: FestipodWorld, elementName: string) {\n this.attach(`Clicked on: ${elementName}`, 'text/plain');\n});",
"lineNumber": 53
},
{
"pattern": "je sélectionne {string}",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je sélectionne {string}', async function (this: FestipodWorld, elementName: string) {\n this.attach(`Selected: ${elementName}`, 'text/plain');\n});",
"lineNumber": 57
},
{
"pattern": "je clique sur le bouton {string}",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je clique sur le bouton {string}', async function (this: FestipodWorld, buttonName: string) {\n this.attach(`Clicked button: ${buttonName}`, 'text/plain');\n});",
"lineNumber": 61
},
{
"pattern": "je clique sur un participant",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je clique sur un participant', async function (this: FestipodWorld) {\n this.navigateTo('#/demo/user-profile');\n});",
"lineNumber": 65
},
{
"pattern": "je clique sur un événement",
"keyword": "When",
"file": "navigation.steps.ts",
"sourceCode": "When('je clique sur un événement', async function (this: FestipodWorld) {\n this.navigateTo('#/demo/event-detail');\n});",
"lineNumber": 69
},
{
"pattern": "je suis redirigé vers {string}",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('je suis redirigé vers {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n expect(this.currentScreenId).to.equal(screenId);\n});",
"lineNumber": 73
},
{
"pattern": "je vois l\\",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('je vois l\\'écran {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n expect(this.currentScreenId).to.equal(screenId);\n});",
"lineNumber": 78
},
{
"pattern": "je reste sur la page {string}",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('je reste sur la page {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n expect(this.currentScreenId).to.equal(screenId);\n});",
"lineNumber": 83
},
{
"pattern": "l\\",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('l\\'écran contient une section {string}', async function (this: FestipodWorld, sectionName: string) {\n expect(this.currentScreenId).to.not.be.null;\n this.attach(`Verified section: ${sectionName}`, 'text/plain');\n});",
"lineNumber": 88
},
{
"pattern": "je peux naviguer vers {string}",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('je peux naviguer vers {string}', async function (this: FestipodWorld, pageName: string) {\n const screenId = resolveScreenId(pageName);\n this.attach(`Navigation available to: ${screenId}`, 'text/plain');\n});",
"lineNumber": 93
},
{
"pattern": "la navigation affiche {string} comme actif",
"keyword": "Then",
"file": "navigation.steps.ts",
"sourceCode": "Then('la navigation affiche {string} comme actif', async function (this: FestipodWorld, menuItem: string) {\n this.attach(`Active menu: ${menuItem}`, 'text/plain');\n});",
"lineNumber": 98
},
{
"pattern": "l\\",
"keyword": "Given",
"file": "form.steps.ts",
"sourceCode": "Given('l\\'écran {string} est affiché', async function (this: FestipodWorld, screenName: string) {\n const screenId = screenName.toLowerCase().replace(/ /g, '-');\n this.navigateTo(`#/demo/${screenId}`);\n});",
"lineNumber": 5
},
{
"pattern": "le formulaire de création est vide",
"keyword": "Given",
"file": "form.steps.ts",
"sourceCode": "Given('le formulaire de création est vide', async function (this: FestipodWorld) {\n this.formFields.forEach((field, key) => {\n this.formFields.set(key, { ...field, value: '' });",
"lineNumber": 10
},
{
"pattern": "je remplis le champ {string} avec {string}",
"keyword": "When",
"file": "form.steps.ts",
"sourceCode": "When('je remplis le champ {string} avec {string}', async function (this: FestipodWorld, fieldName: string, value: string) {\n const existing = this.formFields.get(fieldName);\n this.formFields.set(fieldName, {\n required: existing?.required ?? false,\n value\n });",
"lineNumber": 16
},
{
"pattern": "je laisse le champ {string} vide",
"keyword": "When",
"file": "form.steps.ts",
"sourceCode": "When('je laisse le champ {string} vide', async function (this: FestipodWorld, fieldName: string) {\n const existing = this.formFields.get(fieldName);\n if (existing) {\n this.formFields.set(fieldName, { ...existing, value: '' });",
"lineNumber": 24
},
{
"pattern": "je soumets le formulaire",
"keyword": "When",
"file": "form.steps.ts",
"sourceCode": "When('je soumets le formulaire', async function (this: FestipodWorld) {\n this.attach('Form submitted', 'text/plain');\n});",
"lineNumber": 31
},
{
"pattern": "le formulaire contient le champ obligatoire {string}",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le formulaire contient le champ obligatoire {string}', async function (this: FestipodWorld, fieldName: string) {\n const field = this.formFields.get(fieldName);\n expect(field, `Field \"${fieldName}\" should exist`).to.not.be.undefined;\n expect(field?.required, `Field \"${fieldName}\" should be required`).to.equal(true);\n});",
"lineNumber": 35
},
{
"pattern": "le formulaire contient les champs obligatoires suivants:",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le formulaire contient les champs obligatoires suivants:', async function (this: FestipodWorld, dataTable) {\n const expectedFields = dataTable.raw().flat();\n expectedFields.forEach((fieldName: string) => {\n const field = this.formFields.get(fieldName);\n expect(field, `Field \"${fieldName}\" should exist`).to.not.be.undefined;\n expect(field?.required, `Field \"${fieldName}\" should be required`).to.equal(true);\n });",
"lineNumber": 41
},
{
"pattern": "le champ {string} est facultatif",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le champ {string} est facultatif', async function (this: FestipodWorld, fieldName: string) {\n const field = this.formFields.get(fieldName);\n if (field) {\n expect(field.required).to.equal(false);\n }\n});",
"lineNumber": 50
},
{
"pattern": "le champ {string} affiche {string}",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le champ {string} affiche {string}', async function (this: FestipodWorld, fieldName: string, expectedValue: string) {\n const field = this.formFields.get(fieldName);\n expect(field?.value).to.equal(expectedValue);\n});",
"lineNumber": 57
},
{
"pattern": "le champ {string} est présent",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le champ {string} est présent', async function (this: FestipodWorld, fieldName: string) {\n const field = this.formFields.get(fieldName);\n expect(field, `Field \"${fieldName}\" should exist`).to.not.be.undefined;\n});",
"lineNumber": 62
},
{
"pattern": "une erreur de validation est affichée pour {string}",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('une erreur de validation est affichée pour {string}', async function (this: FestipodWorld, fieldName: string) {\n const field = this.formFields.get(fieldName);\n expect(field?.required).to.equal(true);\n expect(field?.value).to.equal('');\n this.attach(`Validation error for: ${fieldName}`, 'text/plain');\n});",
"lineNumber": 67
},
{
"pattern": "le formulaire affiche {int} champs",
"keyword": "Then",
"file": "form.steps.ts",
"sourceCode": "Then('le formulaire affiche {int} champs', async function (this: FestipodWorld, count: number) {\n expect(this.formFields.size).to.equal(count);\n});",
"lineNumber": 74
},
{
"pattern": "je peux voir la liste des participants",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir la liste des participants', async function (this: FestipodWorld) {\n const screensWithParticipants = ['event-detail', 'participants-list', 'invite'];\n expect(screensWithParticipants, `Screen ${this.currentScreenId} should show participants`).to.include(this.currentScreenId);\n\n // Verify the text \"Participant\" appears in the rendered content\n const hasParticipants = this.hasText('Participant') || this.hasText('participant') || this.hasText('inscrits');\n expect(hasParticipants, 'Page should display participants list').to.be.true;\n});",
"lineNumber": 6
},
{
"pattern": "je peux voir les détails de l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir les détails de l\\'événement', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('event-detail');\n // Verify event detail content is rendered\n const hasEventInfo = this.hasText('Description') || this.hasText('Participant') || this.hasText('inscrits');\n expect(hasEventInfo, 'Event detail page should show event information').to.be.true;\n});",
"lineNumber": 15
},
{
"pattern": "je peux voir la section {string}",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir la section {string}', async function (this: FestipodWorld, sectionName: string) {\n const hasSection = this.hasText(sectionName);\n if (!hasSection) {\n this.attach(`Looking for section: \"${sectionName}\"`, 'text/plain');\n this.attach(`Rendered text: ${this.getRenderedText().substring(0, 500)}...`, 'text/plain');\n }\n expect(hasSection, `Section \"${sectionName}\" should be visible on screen`).to.be.true;\n});",
"lineNumber": 22
},
{
"pattern": "la page affiche {int} éléments",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('la page affiche {int} éléments', async function (this: FestipodWorld, count: number) {\n // This is harder to verify without specific selectors, so we just log it\n this.attach(`Expected ${count} elements displayed`, 'text/plain');\n});",
"lineNumber": 31
},
{
"pattern": "je peux voir mon profil",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir mon profil', async function (this: FestipodWorld) {\n expect(['profile', 'user-profile']).to.include(this.currentScreenId);\n // Verify profile content\n const hasProfileContent = this.hasText('profil') || this.hasText('Profil');\n expect(hasProfileContent, 'Profile page should display profile content').to.be.true;\n});",
"lineNumber": 36
},
{
"pattern": "je peux voir le profil de l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir le profil de l\\'utilisateur', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('user-profile');\n const hasProfileContent = this.hasText('Profil') || this.hasText('@');\n expect(hasProfileContent, 'User profile should display profile information').to.be.true;\n});",
"lineNumber": 43
},
{
"pattern": "je peux voir la liste des événements",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir la liste des événements', async function (this: FestipodWorld) {\n expect(['events', 'home', 'profile']).to.include(this.currentScreenId);\n // Verify events list is shown\n const hasEvents = this.hasText('Événement') || this.hasText('événement') || this.hasText('inscrits');\n expect(hasEvents, 'Page should display events list').to.be.true;\n});",
"lineNumber": 49
},
{
"pattern": "je peux voir le QR code",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir le QR code', async function (this: FestipodWorld) {\n expect(['profile', 'share-profile', 'meeting-points']).to.include(this.currentScreenId);\n // Check for QR code related content\n const hasQRContent = this.hasText('QR') || this.hasText('Partager') || this.hasText('partager');\n expect(hasQRContent, 'Page should have QR code or share functionality').to.be.true;\n});",
"lineNumber": 56
},
{
"pattern": "je peux voir le lien de partage",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir le lien de partage', async function (this: FestipodWorld) {\n expect(['profile', 'share-profile']).to.include(this.currentScreenId);\n const hasShareLink = this.hasText('Partager') || this.hasText('partager') || this.hasText('lien');\n expect(hasShareLink, 'Page should display share link functionality').to.be.true;\n});",
"lineNumber": 63
},
{
"pattern": "un événement existe avec les données:",
"keyword": "Given",
"file": "screen.steps.ts",
"sourceCode": "Given('un événement existe avec les données:', async function (this: FestipodWorld, dataTable) {\n const eventData = dataTable.rowsHash();\n this.attach(`Event data: ${JSON.stringify(eventData)}`, 'text/plain');\n});",
"lineNumber": 69
},
{
"pattern": "un utilisateur existe avec les données:",
"keyword": "Given",
"file": "screen.steps.ts",
"sourceCode": "Given('un utilisateur existe avec les données:', async function (this: FestipodWorld, dataTable) {\n const userData = dataTable.rowsHash();\n this.attach(`User data: ${JSON.stringify(userData)}`, 'text/plain');\n});",
"lineNumber": 74
},
{
"pattern": "je visualise l\\",
"keyword": "Given",
"file": "screen.steps.ts",
"sourceCode": "Given('je visualise l\\'événement {string}', async function (this: FestipodWorld, eventName: string) {\n this.navigateTo('#/demo/event-detail');\n expect(this.currentScreen, 'Event detail screen should be loaded').to.not.be.null;\n this.attach(`Viewing event: ${eventName}`, 'text/plain');\n});",
"lineNumber": 79
},
{
"pattern": "je visualise le profil de {string}",
"keyword": "Given",
"file": "screen.steps.ts",
"sourceCode": "Given('je visualise le profil de {string}', async function (this: FestipodWorld, userName: string) {\n this.navigateTo('#/demo/user-profile');\n expect(this.currentScreen, 'User profile screen should be loaded').to.not.be.null;\n this.attach(`Viewing profile: ${userName}`, 'text/plain');\n});",
"lineNumber": 85
},
{
"pattern": "l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('l\\'écran affiche les informations de l\\'événement', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('event-detail');\n // Verify actual content is rendered\n const expectedContent = screenExpectedContent['event-detail'] || [];\n const renderedText = this.getRenderedText();\n\n let foundCount = 0;\n for (const content of expectedContent) {\n if (renderedText.includes(content)) {\n foundCount++;\n }\n }\n\n expect(foundCount, `At least one expected content item should be present`).to.be.greaterThan(0);\n});",
"lineNumber": 91
},
{
"pattern": "l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('l\\'écran affiche les informations du profil', async function (this: FestipodWorld) {\n expect(['profile', 'user-profile']).to.include(this.currentScreenId);\n // Verify profile info is rendered\n const hasProfileInfo = this.hasText('Profil') || this.hasText('@') || this.hasText('Événement');\n expect(hasProfileInfo, 'Profile information should be displayed').to.be.true;\n});",
"lineNumber": 107
},
{
"pattern": "je peux ajouter un commentaire",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux ajouter un commentaire', async function (this: FestipodWorld) {\n // Check for comment feature using precise detector\n const hasCommentFeature = this.hasField('Commentaire');\n\n if (!hasCommentFeature) {\n this.attach(`MISSING FEATURE: Comment functionality is not implemented in screen \"${this.currentScreenId}\"`, 'text/plain');\n this.attach(`Expected: textarea element or \"commentaire\" text in the screen`, 'text/plain');\n return 'pending'; // Mark as pending instead of failing\n }\n});",
"lineNumber": 114
},
{
"pattern": "je peux ajouter une note",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux ajouter une note', async function (this: FestipodWorld) {\n // Check for note feature - similar to comment\n const hasNoteFeature = this.hasText('Note') || this.hasText('note') || this.hasElement('textarea');\n\n if (!hasNoteFeature) {\n this.attach(`MISSING FEATURE: Note functionality is not implemented in screen \"${this.currentScreenId}\"`, 'text/plain');\n return 'pending';\n }\n});",
"lineNumber": 125
},
{
"pattern": "je peux filtrer les événements par période",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux filtrer les événements par période', async function (this: FestipodWorld) {\n // Check for period filter feature\n const hasPeriodFilter = this.hasText('mois') || this.hasText('trimestre') || this.hasText('année') ||\n this.hasText('période') || this.hasText('Période');\n\n if (!hasPeriodFilter) {\n this.attach(`MISSING FEATURE: Period filter is not implemented in screen \"${this.currentScreenId}\"`, 'text/plain');\n return 'pending';\n }\n});",
"lineNumber": 135
},
{
"pattern": "je peux modifier un commentaire",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux modifier un commentaire', async function (this: FestipodWorld) {\n // Comment editing is typically available where adding is\n const hasEditFeature = this.hasText('Modifier') || this.hasText('modifier') || this.hasElement('button');\n expect(hasEditFeature, 'Edit functionality should be available').to.be.true;\n});",
"lineNumber": 146
},
{
"pattern": "je peux supprimer un commentaire",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux supprimer un commentaire', async function (this: FestipodWorld) {\n // Delete is typically available where edit is\n const hasDeleteFeature = this.hasText('Supprimer') || this.hasText('supprimer') || this.hasElement('button');\n expect(hasDeleteFeature, 'Delete functionality should be available').to.be.true;\n});",
"lineNumber": 152
},
{
"pattern": "je peux m\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux m\\'inscrire à l\\'événement', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('event-detail');\n // Check for registration button\n const hasRegisterFeature = this.hasText('inscription') || this.hasText('Participer') ||\n this.hasText('participer') || this.hasText('S\\'inscrire') ||\n this.hasText('Rejoindre');\n expect(hasRegisterFeature, 'Registration feature should be available').to.be.true;\n});",
"lineNumber": 158
},
{
"pattern": "je peux me désinscrire de l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux me désinscrire de l\\'événement', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('event-detail');\n // Unregister is typically on the same page as register\n const hasUnregisterFeature = this.hasText('désinscri') || this.hasText('Annuler') ||\n this.hasText('Quitter') || this.hasElement('button');\n expect(hasUnregisterFeature, 'Unregister feature should be available').to.be.true;\n});",
"lineNumber": 167
},
{
"pattern": "je peux contacter l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux contacter l\\'utilisateur', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('user-profile');\n // Check for contact functionality\n const hasContactFeature = this.hasText('Contact') || this.hasText('Message') ||\n this.hasText('message') || this.hasElement('button');\n expect(hasContactFeature, 'Contact feature should be available').to.be.true;\n});",
"lineNumber": 175
},
{
"pattern": "je peux voir les événements auxquels l\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux voir les événements auxquels l\\'utilisateur a participé', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('user-profile');\n // Check for user's events\n const hasUserEvents = this.hasText('Événement') || this.hasText('événement') ||\n this.hasText('Participation') || this.hasText('participation');\n expect(hasUserEvents, 'User events should be visible').to.be.true;\n});",
"lineNumber": 183
},
{
"pattern": "je peux configurer mes notifications",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux configurer mes notifications', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('settings');\n // Check for notification settings\n const hasNotificationSetting = this.hasText('Notification') || this.hasText('notification');\n expect(hasNotificationSetting, 'Notification settings should be visible').to.be.true;\n});",
"lineNumber": 191
},
{
"pattern": "je peux définir mon rayon de notification",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux définir mon rayon de notification', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('settings');\n // Check for location/radius setting\n const hasRadiusSetting = this.hasText('Localisation') || this.hasText('localisation') ||\n this.hasText('rayon') || this.hasText('Rayon');\n expect(hasRadiusSetting, 'Location/radius setting should be visible').to.be.true;\n});",
"lineNumber": 198
},
{
"pattern": "je peux définir mes thématiques d\\",
"keyword": "Then",
"file": "screen.steps.ts",
"sourceCode": "Then('je peux définir mes thématiques d\\'intérêt', async function (this: FestipodWorld) {\n expect(this.currentScreenId).to.equal('settings');\n // Settings page should allow configuring interests (or it could be on profile)\n // For now just verify we're on settings\n expect(this.currentScreen, 'Settings screen should be loaded').to.not.be.null;\n});",
"lineNumber": 206
}
];
export function findStepDefinition(stepText: string): StepDefinitionInfo | null {
for (const def of stepDefinitions) {
// Convert Cucumber expression to regex
// {string} -> "[^"]+"
// {int} -> \\d+
const regexPattern = def.pattern
.replace(/\{string\}/g, '"[^"]+"')
.replace(/\{int\}/g, '\\d+');
try {
const regex = new RegExp(regexPattern);
if (regex.test(stepText)) {
return def;
}
} catch {
// If pattern fails, try simple includes
const simplified = def.pattern.replace(/\{string\}/g, '').replace(/\{int\}/g, '').trim();
if (stepText.includes(simplified)) {
return def;
}
}
}
return null;
}