mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix: match architecture handtest identifiers literally
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
8c4747666c
commit
e30dd59fde
3 files changed
+41
-2
No files matched your search
@@ -33,6 +33,7 @@
|
||||
*/
|
||||
|
||||
import { chromium } from 'playwright';
|
||||
import { architectureNodeSelector, projectNamePattern } from './handtest-locators.mjs';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { inflateSync } from 'node:zlib';
|
||||
@@ -446,7 +447,7 @@ async function hoverSweep(page) {
|
||||
const hid = [];
|
||||
let shown = false;
|
||||
for (const id of ids.slice(0, 14)) {
|
||||
const chip = page.locator(`button.architecture-node-label[data-node-id="${id.replace(/"/g, '\\"')}"]`).first();
|
||||
const chip = page.locator(architectureNodeSelector(id)).first();
|
||||
if (!(await chip.isVisible().catch(() => false))) continue;
|
||||
await chip.hover({ timeout: 3000 }).catch(() => {});
|
||||
await wait(450);
|
||||
@@ -498,7 +499,7 @@ async function k21(page) {
|
||||
|
||||
async function switchProject(page, name) {
|
||||
await page.locator('.atlas-shell details summary', { hasText: PROJECT }).first().click();
|
||||
const entry = page.locator('.atlas-project-results button', { has: page.locator('.atlas-project-result-name', { hasText: new RegExp(`^${name}$`) }) }).first();
|
||||
const entry = page.locator('.atlas-project-results button', { has: page.locator('.atlas-project-result-name', { hasText: projectNamePattern(name) }) }).first();
|
||||
await entry.waitFor({ timeout: 20000 });
|
||||
await entry.click();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { literalPattern } from './literal-pattern.mjs';
|
||||
|
||||
/** Match one architecture chip by its graph node ID. */
|
||||
export function architectureNodeSelector(id) {
|
||||
// CSS strings need escapes for backslashes, quotes, and control characters.
|
||||
// Terminate each hex escape so the next character cannot extend its code point.
|
||||
const escaped = id.replace(/[\x00-\x1f\x7f"\\]/g, char => `\\${char.codePointAt(0).toString(16)} `);
|
||||
return `button.architecture-node-label[data-node-id="${escaped}"]`;
|
||||
}
|
||||
|
||||
/** Match a complete project name in the project chooser. */
|
||||
export function projectNamePattern(name) {
|
||||
return literalPattern(name, { start: true, end: true });
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { expect, it } from 'vitest';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import { architectureNodeSelector, projectNamePattern } from './handtest-locators.mjs';
|
||||
|
||||
it.each(['node', 'node"name', 'node\\name', 'node\\"name', 'node\nname', 'node\rname', 'node\fname', 'node], button'])('selects only the literal graph node ID: %j', id => {
|
||||
const { document } = new JSDOM('<button class="architecture-node-label" data-node-id="other"></button>').window;
|
||||
const target = document.createElement('button');
|
||||
target.className = 'architecture-node-label';
|
||||
target.setAttribute('data-node-id', id);
|
||||
document.body.append(target);
|
||||
expect(document.querySelectorAll(architectureNodeSelector(id))).toHaveLength(1);
|
||||
expect(document.querySelector(architectureNodeSelector(id))).toBe(target);
|
||||
});
|
||||
|
||||
it.each(['a.b', 'a+b', '(project)', '[project]', 'a\\b', '*', '(a+)+$'])('matches a complete literal project name: %j', name => {
|
||||
const pattern = projectNamePattern(name);
|
||||
expect(pattern.test(name)).toBe(true);
|
||||
expect(pattern.test(`prefix${name}`)).toBe(false);
|
||||
expect(pattern.test(`${name}suffix`)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not treat a project-name dot as a wildcard', () => {
|
||||
expect(projectNamePattern('a.b').test('axb')).toBe(false);
|
||||
});
|
||||
Reference in new issue
Block a user