pre work commit
This commit is contained in:
155
server.ts
Normal file
155
server.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
// server.ts - Local server that receives click data from the Chrome extension
|
||||
// Run with: ts-node --transpile-only server.ts
|
||||
|
||||
import * as http from "http";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
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<string, string> {
|
||||
const files = fs.readdirSync(__dirname).filter(f => f.endsWith(".po.ts"));
|
||||
|
||||
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<string, string> = {};
|
||||
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;
|
||||
}
|
||||
|
||||
function findMatches(data: any, selectors: Record<string, string>): string[] {
|
||||
const results: string[] = [];
|
||||
|
||||
for (const [name, selector] of Object.entries(selectors)) {
|
||||
if (selector.startsWith(".")) {
|
||||
const className = selector.replace(".", "");
|
||||
if (data.classes.includes(className)) {
|
||||
results.push(name);
|
||||
}
|
||||
}
|
||||
if (data.ariaLabel && selector === data.ariaLabel) {
|
||||
results.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function appendToOutput(matches: string[]) {
|
||||
const lines = matches.map(name => `mainpage.${name}.click();`);
|
||||
fs.appendFileSync(OUTPUT_FILE, lines.join("\n") + "\n");
|
||||
}
|
||||
|
||||
function appendSuggestion(data: any) {
|
||||
const firstClass = data.classes.length > 0 ? data.classes[0] : null;
|
||||
|
||||
// Generate a name from aria-label or first class
|
||||
const name = 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 (use whichever selector fits best):`);
|
||||
|
||||
// Prefer aria > id > class for the get function selector variable
|
||||
const selectorVar = data.ariaLabel
|
||||
? `Mainpage.${name}aria`
|
||||
: data.id
|
||||
? `Mainpage.${name}id`
|
||||
: `Mainpage.${name}class`;
|
||||
|
||||
lines.push(`get ${name}() {`);
|
||||
lines.push(` return cy.get(${selectorVar});`);
|
||||
lines.push(`}`);
|
||||
lines.push(``);
|
||||
lines.push(`// ------------------------------------------------`);
|
||||
lines.push(``);
|
||||
|
||||
fs.appendFileSync(SUGGESTIONS_FILE, lines.join("\n") + "\n");
|
||||
}
|
||||
|
||||
// Load selectors once at startup
|
||||
const selectors = loadPageObject();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
|
||||
if (req.method === "OPTIONS") {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && req.url === "/record") {
|
||||
let body = "";
|
||||
|
||||
req.on("data", (chunk) => (body += chunk));
|
||||
req.on("end", () => {
|
||||
try {
|
||||
const data = JSON.parse(body);
|
||||
|
||||
console.log("\n--- Click Recorded ---");
|
||||
console.log("Tag: ", data.tag);
|
||||
console.log("ID: ", data.id);
|
||||
console.log("Aria-label: ", data.ariaLabel);
|
||||
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);
|
||||
}
|
||||
|
||||
console.log("----------------------");
|
||||
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok" }));
|
||||
} catch (err: any) {
|
||||
console.error("Failed to parse body:", err.message);
|
||||
res.writeHead(400);
|
||||
res.end("Bad request");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end("Not found");
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Cypress Recorder server running on http://localhost:${PORT}`);
|
||||
});
|
||||
Reference in New Issue
Block a user