diff --git a/mainpage.po.ts b/mainpage.po.ts index 847f61e..40248cc 100644 --- a/mainpage.po.ts +++ b/mainpage.po.ts @@ -1,231 +1,47 @@ import { Util } from "../util"; export class Mainpage { - static button = ".mdc-button" - static menubutton = ".mat-mdc-menu-item" - static menutextbutton = ".mat-mdc-menu-item-text" - static openmenu = ".mat-mdc-menu-trigger" - static navbuttons = ".mat-mdc-tooltip-trigger" - static inputfield = ".mat-mdc-input-element" - static panelbutton = ".cmdsk-wlcm__panel-action" //cmdsk-wlcm__panel-action - static yuvform = ".yuv-object-form-element" - static autocomplete = ".mat-mdc-autocomplete-panel" - static calender = ".calender" - static checkbox = ".mdc-checkbox" - static formcategory = ".main" - static buttontext = ".mdc-button__label" - static tab = ".mdc-tab" - static casefunction = ".mdc-evolution-chip__action" - static documentmenubutton = ".cdk-menu-item" - static searchfield = ".search" - static loadingspinner = ".mat-mdc-progress-spinner" - static listitem = ".mdc-list-item" - static tile = ".tile" + static button = ".dummy" + static matmdctooltiptriggerclass = ".mat-mdc-tooltip-trigger" + static matbuttontogglebuttonclass = ".mat-button-toggle-button" + static matmdcinputelementclass = ".mat-mdc-input-element" + static startsearcharia = "[aria-label='Start search']" - static treenode = ".mat-tree-node" - - get Uploadbutton() { - return cy.nccontains(Mainpage.menubutton, 'Upload document'); - } - - get Casecreatebutton() { - return cy.nccontains(Mainpage.menubutton, ' Create case '); - } - - get Openbutton() { - return cy.nccontains(Mainpage.openmenu, "add"); - } - - get Navbuttons() { - return cy.get(Mainpage.navbuttons); - } - - get Homebutton(){ - return cy.nccontains(Mainpage.navbuttons, 'home') - } - - get Casenavbutton(){ - return cy.nccontains(Mainpage.navbuttons, 'cases') - } - - get Dokumentenavbutton(){ - return cy.nccontains(Mainpage.navbuttons, 'description') - } - - get Deletebutton(){ - return cy.nccontains(Mainpage.documentmenubutton, 'delete') - } - - Casedeletionbutton(selector: string){ - return cy.contains(selector).parent().parent().parent().find(Mainpage.navbuttons).eq(0) - } - - - get Kebabbutton(){ - return cy.ncnccontains(Mainpage.openmenu, 'more_vert') - } - - get Deleteworkspacebutton(){ - return cy.nccontains(Mainpage.menubutton, 'Delete workspace') - } - - get Confirmbutton(){ - return cy.nccontains(Mainpage.button, 'Confirm') - } - - get Deleteconfirmbutton(){ - return cy.nccontains(Mainpage.button, 'Delete') - } - - get Inputfield() { - return cy.get(Mainpage.inputfield); - } - - get Panelbutton() { - return cy.get(Mainpage.panelbutton); - } - - get CasePanelbutton() { - return cy.nccontains(Mainpage.panelbutton, 'case') - } - - get Yuvform(){ - return cy.get(Mainpage.yuvform) - } - - Caseform(selector: string){ - return cy.nccontains(Mainpage.yuvform, selector) - } - - get Autocomplete(){ - return cy.get(Mainpage.autocomplete) - } - - Caseformcalender(selector: string){ - return cy.nccontains(Mainpage.yuvform, selector).find(Mainpage.calender) - } - - get Formdateselect(){ - return cy.nccontains('button', "Select") - } - - get Checkbox(){ - return cy.get(Mainpage.checkbox) - } - - - Formcategory(selector: string){ - return cy.nccontains(Mainpage.formcategory, selector) - } - - get Formconfirm(){ - return cy.nccontains('button', /^ Create $/i) - } - - get Historytab(){ - return cy.nccontains(Mainpage.tab, "history") - } - - get Metadatatab(){ - return cy.nccontains(Mainpage.tab, "Metadata") - } - - get Renamebutton(){ - return cy.nccontains(Mainpage.navbuttons, "Rename") - } - - get Renamefield(){ - return cy.get('yuv-string[formcontrolname="name"] input') - } - - get Renameconfirm(){ - return cy.nccontains(Mainpage.button, "Rename") - } - - - CheckAIfunction(wantedfunction: string){ - return cy.nccontains(Mainpage.casefunction, wantedfunction) - } - - CheckAIlogo(){ - return cy.nccontains("robot_2") - } - - get sortingbutton(){ - return cy.nccontains("swap_vert") - } + - get firsttreenode(){ - return cy.get('button[mattreenodetoggle]').eq(0) - } - get Metadatainput(){ - return cy.get('[data-name="appCasem:description"]').eq(1) - } - get Metadatainputsearch(){ - return cy.get('[data-name="appCasem:description"]').eq(3) - } - - get Metadataconfirm(){ - return cy.nccontains('button', /^ Save $/i) - } - Suchedokument(name: string){ - cy.get(Mainpage.searchfield).type(name) - cy.get('[aria-label="Start search"]').click( {force: true} ) - } - Waitforloading(){ - cy.get(Mainpage.loadingspinner).should("not.exist"); - } - get firstaction(){ - return cy.get('[title="Actions for Processphase: Registration"]') - } - get translatedocument(){ - return cy.get('[aria-label="Execute Action: Translate document"]') - } - get startaction(){ - return cy.nccontains(Mainpage.menutextbutton, "Create action") + get startsearchsearch() { + return cy.nccontains(Mainpage.startsearcharia, 'search'); } - - get adddokument(){ - return cy.nccontains(" Add document to action ") + get matmdcinputelement() { + return cy.get(Mainpage.matmdcinputelementclass); } - - get adddokumenttocase(){ - return cy.nccontains(" Add to case ") + get matmdctooltiptriggercloud_circle() { + return cy.nccontains(Mainpage.matmdctooltiptriggerclass, 'cloud_circle'); } - - get confirmtranslate(){ - return cy.nccontains('button', "Translate") + get matmdctooltiptriggerlibrary_books() { + return cy.nccontains(Mainpage.matmdctooltiptriggerclass, 'library_books'); } - - Checkfirststarted(){ - cy.get('[class="node exists"]').get('[title="Actions for Processphase: Registration"]') + get matmdctooltiptriggermove_to_inbox() { + return cy.nccontains(Mainpage.matmdctooltiptriggerclass, 'move_to_inbox'); } - - get fileinput(){ - return cy.get('input[type="file"]') + get matbuttontogglebuttondark() { + return cy.nccontains(Mainpage.matbuttontogglebuttonclass, 'Dark'); } - - get attachdokumentclass(){ - return cy.get('[id="mat-select-0"]') + get matbuttontogglebuttonsystem() { + return cy.nccontains(Mainpage.matbuttontogglebuttonclass, 'System'); } - - get listitem(){ - return cy.get(Mainpage.listitem) - } - - get tile(){ - return cy.get(Mainpage.tile) - } - - get notification(){ - return cy.nccontains("notifications") + get matmdctooltiptriggersettings() { + return cy.nccontains(Mainpage.matmdctooltiptriggerclass, 'settings'); } + get dummybutton() { + return cy.nccontains(Mainpage.menubutton, 'Upload document'); + } } \ No newline at end of file diff --git a/mainpage.po.ts_empty b/mainpage.po.ts_empty deleted file mode 100644 index 9171074..0000000 --- a/mainpage.po.ts_empty +++ /dev/null @@ -1,5 +0,0 @@ -import { Util } from "../util"; - -export class Mainpage { - -} \ No newline at end of file diff --git a/mainpage.po.ts_something b/mainpage.po.ts_something new file mode 100644 index 0000000..b25781c --- /dev/null +++ b/mainpage.po.ts_something @@ -0,0 +1,232 @@ +import { Util } from "../util"; + +export class Mainpage { + static button = ".mdc-button" + static menubutton = ".mat-mdc-menu-item" + static menutextbutton = ".mat-mdc-menu-item-text" + static openmenu = ".mat-mdc-menu-trigger" + static navbuttons = ".mat-mdc-tooltip-trigger" + static inputfield = ".mat-mdc-input-element" + static panelbutton = ".cmdsk-wlcm__panel-action" //cmdsk-wlcm__panel-action + static yuvform = ".yuv-object-form-element" + static autocomplete = ".mat-mdc-autocomplete-panel" + static calender = ".calender" + static checkbox = ".mdc-checkbox" + static formcategory = ".main" + static buttontext = ".mdc-button__label" + static tab = ".mdc-tab" + static casefunction = ".mdc-evolution-chip__action" + static documentmenubutton = ".cdk-menu-item" + static searchfield = ".search" + static loadingspinner = ".mat-mdc-progress-spinner" + static listitem = ".mdc-list-item" + static tile = ".tile" + + static treenode = ".mat-tree-node" + + + get Uploadbutton() { + return cy.nccontains(Mainpage.menubutton, 'Upload document'); + } + + get Casecreatebutton() { + return cy.nccontains(Mainpage.menubutton, ' Create case '); + } + + get Openbutton() { + return cy.nccontains(Mainpage.openmenu, "add"); + } + + get Navbuttons() { + return cy.get(Mainpage.navbuttons); + } + + get Homebutton(){ + return cy.nccontains(Mainpage.navbuttons, 'home') + } + + get Casenavbutton(){ + return cy.nccontains(Mainpage.navbuttons, 'cases') + } + + get Dokumentenavbutton(){ + return cy.nccontains(Mainpage.navbuttons, 'description') + } + + get Deletebutton(){ + return cy.nccontains(Mainpage.documentmenubutton, 'delete') + } + + Casedeletionbutton(selector: string){ + return cy.contains(selector).parent().parent().parent().find(Mainpage.navbuttons).eq(0) + } + + + get Kebabbutton(){ + return cy.ncnccontains(Mainpage.openmenu, 'more_vert') + } + + get Deleteworkspacebutton(){ + return cy.nccontains(Mainpage.menubutton, 'Delete workspace') + } + + get Confirmbutton(){ + return cy.nccontains(Mainpage.button, 'Confirm') + } + + get Deleteconfirmbutton(){ + return cy.nccontains(Mainpage.button, 'Delete') + } + + get Inputfield() { + return cy.get(Mainpage.inputfield); + } + + get Panelbutton() { + return cy.get(Mainpage.panelbutton); + } + + get CasePanelbutton() { + return cy.nccontains(Mainpage.panelbutton, 'case') + } + + get Yuvform(){ + return cy.get(Mainpage.yuvform) + } + + Caseform(selector: string){ + return cy.nccontains(Mainpage.yuvform, selector) + } + + get Autocomplete(){ + return cy.get(Mainpage.autocomplete) + } + + Caseformcalender(selector: string){ + return cy.nccontains(Mainpage.yuvform, selector).find(Mainpage.calender) + } + + get Formdateselect(){ + return cy.nccontains('button', "Select") + } + + get Checkbox(){ + return cy.get(Mainpage.checkbox) + } + + + Formcategory(selector: string){ + return cy.nccontains(Mainpage.formcategory, selector) + } + + get Formconfirm(){ + return cy.nccontains('button', /^ Create $/i) + } + + get Historytab(){ + return cy.nccontains(Mainpage.tab, "history") + } + + get Metadatatab(){ + return cy.nccontains(Mainpage.tab, "Metadata") + } + + get Renamebutton(){ + return cy.nccontains(Mainpage.navbuttons, "Rename") + } + + get Renamefield(){ + return cy.get('yuv-string[formcontrolname="name"] input') + } + + get Renameconfirm(){ + return cy.nccontains(Mainpage.button, "Rename") + } + + + CheckAIfunction(wantedfunction: string){ + return cy.nccontains(Mainpage.casefunction, wantedfunction) + } + + CheckAIlogo(){ + return cy.nccontains("robot_2") + } + + get sortingbutton(){ + return cy.nccontains("swap_vert") + } + + + get firsttreenode(){ + return cy.get('button[mattreenodetoggle]').eq(0) + } + + get Metadatainput(){ + return cy.get('[data-name="appCasem:description"]').eq(1) + } + + get Metadatainputsearch(){ + return cy.get('[data-name="appCasem:description"]').eq(3) + } + + get Metadataconfirm(){ + return cy.nccontains('button', /^ Save $/i) + } + + Suchedokument(name: string){ + cy.get(Mainpage.searchfield).type(name) + cy.get('[aria-label="Start search"]').click( {force: true} ) + } + + Waitforloading(){ + cy.get(Mainpage.loadingspinner).should("not.exist"); + } + + get firstaction(){ + return cy.get('[title="Actions for Processphase: Registration"]') + } + + get translatedocument(){ + return cy.get('[aria-label="Execute Action: Translate document"]') + } + + get startaction(){ + return cy.nccontains(Mainpage.menutextbutton, "Create action") + } + + get adddokument(){ + return cy.nccontains(" Add document to action ") + } + + get adddokumenttocase(){ + return cy.nccontains(" Add to case ") + } + + get confirmtranslate(){ + return cy.nccontains('button', "Translate") + } + + Checkfirststarted(){ + cy.get('[class="node exists"]').get('[title="Actions for Processphase: Registration"]') + } + + get fileinput(){ + return cy.get('input[type="file"]') + } + + get attachdokumentclass(){ + return cy.get('[id="mat-select-0"]') + } + + get listitem(){ + return cy.get(Mainpage.listitem) + } + + get tile(){ + return cy.get(Mainpage.tile) + } + + get notification(){ + return cy.nccontains("notifications") + } +} \ No newline at end of file diff --git a/recorded_clicks.ts b/recorded_clicks.ts index 886be8b..5b01c4a 100644 --- a/recorded_clicks.ts +++ b/recorded_clicks.ts @@ -1,13 +1,13 @@ -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.openmenu.click(); -mainpage.menubutton.click(); -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.treenode.click(); -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.navbuttons.click(); -mainpage.inputfield.click(); +mainpage.matmdctooltiptriggersettings.click(); +mainpage.matbuttontogglebuttonsystem.click(); +mainpage.matbuttontogglebuttonclass.click(); +mainpage.schreibensieihreanfrageanclaude.click(); +mainpage.fontbaseboldkopieren.click(); +mainpage.matmdctooltiptriggersettings.click(); +mainpage.matbuttontogglebuttonsystem.click(); +mainpage.matbuttontogglebuttondark.click(); +mainpage.matmdctooltiptriggermove_to_inbox.click(); +mainpage.matmdctooltiptriggerlibrary_books.click(); +mainpage.matmdctooltiptriggercloud_circle.click(); +mainpage.matmdcinputelement.click(); +mainpage.startsearchsearch.click(); diff --git a/server.ts b/server.ts index 80fcf6f..c239935 100644 --- a/server.ts +++ b/server.ts @@ -9,99 +9,259 @@ const PORT = 3000; const OUTPUT_FILE = path.join(__dirname, "recorded_clicks.ts"); const SUGGESTIONS_FILE = path.join(__dirname, "suggestions.txt"); -// Dynamically find and load the first .po.ts file in the same folder -function loadPageObject(): Record { - const files = fs.readdirSync(__dirname).filter(f => f.endsWith(".po.ts")); +// ─── Parse .po.ts file into structured data ─────────────────────────────────── - if (files.length === 0) { - console.warn("No .po.ts file found in server directory!"); - return {}; - } - - const filePath = path.join(__dirname, files[0]); - console.log(`Loading page object from: ${filePath}`); - - const mod = require(filePath); - const exportedClass = Object.values(mod)[0] as any; - - const selectors: Record = {}; - for (const [key, value] of Object.entries(exportedClass)) { - if (typeof value === "string") { - selectors[key] = value as string; - } - } - - console.log(`Loaded ${Object.keys(selectors).length} selectors.`); - return selectors; +interface ParsedPageObject { + filePath: string; + statics: Record; // name -> selector value + getFunctions: ParsedGetFn[]; } -function findMatches(data: any, selectors: Record): string[] { - const results: string[] = []; +interface ParsedGetFn { + name: string; + body: string; // full body text + referencedStatics: string[]; // static variable names used in body + nccontainsText: string | null; // text in nccontains() if present +} - for (const [name, selector] of Object.entries(selectors)) { - if (selector.startsWith(".")) { - const className = selector.replace(".", ""); - if (data.classes.includes(className)) { - results.push(name); +function parsePageObject(): ParsedPageObject { + const poFiles = fs.readdirSync(__dirname).filter(f => f.endsWith(".po.ts")); + if (poFiles.length === 0) throw new Error("No .po.ts file found!"); + + const filePath = path.join(__dirname, poFiles[0]); + const content = fs.readFileSync(filePath, "utf-8"); + const lines = content.split("\n"); + + const statics: Record = {}; + const getFunctions: ParsedGetFn[] = []; + + let i = 0; + while (i < lines.length) { + const line = lines[i].trimStart(); + + // Parse static declarations: static foo = ".some-class" + const staticMatch = line.match(/^static\s+(\w+)\s*=\s*["'](.+?)["']/); + if (staticMatch) { + statics[staticMatch[1]] = staticMatch[2]; + i++; + continue; + } + + // Parse get functions: get FooBar() { + const getMatch = line.match(/^get\s+(\w+)\s*\(/); + if (getMatch) { + const fnName = getMatch[1]; + const bodyLines: string[] = []; + let depth = 0; + while (i < lines.length) { + const l = lines[i]; + bodyLines.push(l); + for (const ch of l) { + if (ch === "{") depth++; + if (ch === "}") depth--; + } + i++; + if (depth === 0 && bodyLines.length > 1) break; } + const body = bodyLines.join("\n"); + + // Find which static variables are referenced + const referencedStatics = Object.keys(statics).filter(s => + body.includes(s) + ); + + // Extract nccontains text if present: nccontains(Mainpage.foo, 'some text') + const ncMatch = body.match(/nccontains\s*\([^,]+,\s*['"](.+?)['"]\s*\)/); + const nccontainsText = ncMatch ? ncMatch[1] : null; + + getFunctions.push({ name: fnName, body, referencedStatics, nccontainsText }); + continue; } - if (data.ariaLabel && selector === data.ariaLabel) { - results.push(name); + + i++; + } + + console.log(`Parsed ${Object.keys(statics).length} statics, ${getFunctions.length} get functions.`); + return { filePath, statics, getFunctions }; +} + +// ─── Matching logic ──────────────────────────────────────────────────────────── + +interface MatchResult { + type: "full_match" | "static_only" | "no_match"; + fnName?: string; // for full_match + staticName?: string; // for static_only and no_match (if static exists) + selectorVar?: string; // for static_only (to build the new get fn) +} + +function findMatch(data: any, po: ParsedPageObject): MatchResult { + const rawText = data.text ? data.text.split("\n")[0].trim() : null; + + // Step 1: find matching static names by class or aria-label + const matchingStatics: string[] = []; + for (const [name, value] of Object.entries(po.statics)) { + const classMatch = value.startsWith(".") && data.classes.includes(value.slice(1)); + const ariaMatch = data.ariaLabel && value === data.ariaLabel; + if (classMatch || ariaMatch) { + matchingStatics.push(name); } } - return results; + if (matchingStatics.length === 0) { + return { type: "no_match" }; + } + + // Step 2: find a get function that references one of those statics AND has matching text + for (const fn of po.getFunctions) { + const referencesMatch = fn.referencedStatics.some(s => matchingStatics.includes(s)); + if (!referencesMatch) continue; + + // If we have text, require nccontains to match it + if (rawText) { + if (fn.nccontainsText && fn.nccontainsText.toLowerCase() === rawText.toLowerCase()) { + return { type: "full_match", fnName: fn.name }; + } + } else { + // No text — any get function referencing the static is good enough + return { type: "full_match", fnName: fn.name }; + } + } + + // Static matched but no get function with matching text found + return { + type: "static_only", + staticName: matchingStatics[0], + selectorVar: `Mainpage.${matchingStatics[0]}`, + }; } -function appendToOutput(matches: string[]) { - const lines = matches.map(name => `mainpage.${name}.click();`); - fs.appendFileSync(OUTPUT_FILE, lines.join("\n") + "\n"); +// ─── File insertion ──────────────────────────────────────────────────────────── + +function insertGetFunction(filePath: string, getBlock: string) { + const content = fs.readFileSync(filePath, "utf-8"); + const fileLines = content.split("\n"); + + // Find the last static line + let lastStaticIndex = -1; + for (let i = 0; i < fileLines.length; i++) { + if (fileLines[i].trimStart().startsWith("static ")) lastStaticIndex = i; + } + + // Find first get after static block + let firstGetIndex = -1; + for (let i = lastStaticIndex + 1; i < fileLines.length; i++) { + if (fileLines[i].trimStart().startsWith("get ")) { firstGetIndex = i; break; } + } + + if (firstGetIndex === -1) { + console.warn("Could not find first get function, aborting insert."); + return; + } + + const getLines = getBlock.split("\n"); + fileLines.splice(firstGetIndex, 0, "", ...getLines); + fs.writeFileSync(filePath, fileLines.join("\n"), "utf-8"); } -function appendSuggestion(data: any) { +function insertStaticAndGet(filePath: string, staticLine: string, getBlock: string) { + const content = fs.readFileSync(filePath, "utf-8"); + const fileLines = content.split("\n"); + + // Find last static line + let lastStaticIndex = -1; + for (let i = 0; i < fileLines.length; i++) { + if (fileLines[i].trimStart().startsWith("static ")) lastStaticIndex = i; + } + + if (lastStaticIndex === -1) { + console.warn("Could not find static block, aborting insert."); + return; + } + + // Find first get after static block + let firstGetIndex = -1; + for (let i = lastStaticIndex + 1; i < fileLines.length; i++) { + if (fileLines[i].trimStart().startsWith("get ")) { firstGetIndex = i; break; } + } + + if (firstGetIndex === -1) { + console.warn("Could not find first get function, aborting insert."); + return; + } + + // Insert static + fileLines.splice(lastStaticIndex + 1, 0, ` ${staticLine}`); + + // Insert get (shifted by 1) + const getLines = getBlock.split("\n"); + fileLines.splice(firstGetIndex + 1, 0, "", ...getLines); + + fs.writeFileSync(filePath, fileLines.join("\n"), "utf-8"); +} + +// ─── Main handler ────────────────────────────────────────────────────────────── + +function buildGetFnName(data: any): string { + const rawText = data.text ? data.text.split("\n")[0].trim() : null; const firstClass = data.classes.length > 0 ? data.classes[0] : null; - - // Generate a name from aria-label or first class - const name = data.ariaLabel + const baseName = data.ariaLabel ? data.ariaLabel.replace(/\s+/g, "").toLowerCase() : firstClass ? firstClass.replace(/[-_.]/g, "").toLowerCase() : "unknown"; - - const lines: string[] = []; - lines.push(`// --- Unmatched click (tag: <${data.tag}>, text: "${data.text}") ---`); - lines.push(`// Check if these exist and add to your page object:`); - if (firstClass) lines.push(`static ${name}class = ".${firstClass}"`); - if (data.id) lines.push(`static ${name}id = "#${data.id}"`); - if (data.ariaLabel) lines.push(`static ${name}aria = "[aria-label='${data.ariaLabel}']"`); - lines.push(``); - lines.push(`// Suggested get function:`); - - // Selector: aria if available, otherwise class - const selectorVar = data.ariaLabel ? `Mainpage.${name}aria` : `Mainpage.${name}class`; - - // Always include text and tag if available - const hasText = data.text && data.text.length > 0; - const filterParts: string[] = []; - if (data.tag) filterParts.push(`data.tag: "${data.tag}"`); - if (hasText) filterParts.push(`text: "${data.text}"`); - - lines.push(`get ${name}() {`); - if (hasText) { - lines.push(` return cy.nccontains(${selectorVar}, '${data.text}');`); - } else { - lines.push(` return cy.get(${selectorVar});`); - } - lines.push(`}`); - lines.push(``); - lines.push(`// ------------------------------------------------`); - lines.push(``); - - fs.appendFileSync(SUGGESTIONS_FILE, lines.join("\n") + "\n"); + return rawText ? baseName + rawText.replace(/\s+/g, "").toLowerCase() : baseName; } -// Load selectors once at startup -const selectors = loadPageObject(); +function handleClick(data: any) { + const po = parsePageObject(); + const match = findMatch(data, po); + const rawText = data.text ? data.text.split("\n")[0].trim() : null; + const hasText = rawText && rawText.length > 0; + const firstClass = data.classes.length > 0 ? data.classes[0] : null; + + if (match.type === "full_match") { + // Perfect — use existing function + console.log(` >> Full match: mainpage.${match.fnName}.click();`); + fs.appendFileSync(OUTPUT_FILE, `mainpage.${match.fnName}.click();\n`); + + } else if (match.type === "static_only") { + // Static exists, just create the get function + const getFnName = buildGetFnName(data); + const getBody = hasText + ? ` return cy.nccontains(${match.selectorVar}, '${rawText}');` + : ` return cy.get(${match.selectorVar});`; + const getBlock = ` get ${getFnName}() {\n${getBody}\n }`; + + console.log(` >> Static match, inserting get function: ${getFnName}`); + insertGetFunction(po.filePath, getBlock); + fs.appendFileSync(OUTPUT_FILE, `mainpage.${getFnName}.click();\n`); + fs.appendFileSync(SUGGESTIONS_FILE, `// Inserted get function (static already existed)\n${getBlock}\n// ----\n\n`); + + } else { + // No match at all — create both static and get function + const baseName = data.ariaLabel + ? data.ariaLabel.replace(/\s+/g, "").toLowerCase() + : firstClass + ? firstClass.replace(/[-_.]/g, "").toLowerCase() + : "unknown"; + const getFnName = buildGetFnName(data); + const selectorVar = data.ariaLabel ? `Mainpage.${baseName}aria` : `Mainpage.${baseName}class`; + const staticValue = data.ariaLabel ? `"[aria-label='${data.ariaLabel}']"` : `".${firstClass}"`; + const staticName = data.ariaLabel ? `${baseName}aria` : `${baseName}class`; + const staticLine = `static ${staticName} = ${staticValue}`; + const getBody = hasText + ? ` return cy.nccontains(${selectorVar}, '${rawText}');` + : ` return cy.get(${selectorVar});`; + const getBlock = ` get ${getFnName}() {\n${getBody}\n }`; + + console.log(` >> No match, inserting static + get function: ${getFnName}`); + insertStaticAndGet(po.filePath, staticLine, getBlock); + fs.appendFileSync(OUTPUT_FILE, `mainpage.${getFnName}.click();\n`); + fs.appendFileSync(SUGGESTIONS_FILE, `// Inserted static + get\n ${staticLine}\n${getBlock}\n// ----\n\n`); + } +} + +// ─── Server ──────────────────────────────────────────────────────────────────── const server = http.createServer((req, res) => { res.setHeader("Access-Control-Allow-Origin", "*"); @@ -116,7 +276,6 @@ const server = http.createServer((req, res) => { if (req.method === "POST" && req.url === "/record") { let body = ""; - req.on("data", (chunk) => (body += chunk)); req.on("end", () => { try { @@ -129,19 +288,9 @@ const server = http.createServer((req, res) => { console.log("Classes: ", data.classes.join(", ")); console.log("Text: ", data.text); - const matches = findMatches(data, selectors); - - if (matches.length > 0) { - console.log("Matches:"); - matches.forEach(m => console.log(` >> mainpage.${m}.click();`)); - appendToOutput(matches); - } else { - console.log(" >> No match — writing to suggestions.txt"); - appendSuggestion(data); - } + handleClick(data); console.log("----------------------"); - res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ status: "ok" })); } catch (err: any) { @@ -158,4 +307,5 @@ const server = http.createServer((req, res) => { server.listen(PORT, () => { console.log(`Cypress Recorder server running on http://localhost:${PORT}`); + console.log(`Watching for clicks...`); }); \ No newline at end of file diff --git a/suggestions.txt b/suggestions.txt index e69de29..0e679da 100644 --- a/suggestions.txt +++ b/suggestions.txt @@ -0,0 +1,80 @@ +// --- Inserted (tag: