test(eeschema): guard URL-detection wxRegEx UTF-8 fix + bump wxwidgets
Add tests/kicad/eeschema-url-regex.spec.ts: opens a text_box-bearing schematic with a real URL via Module.kicadOpenFile and asserts the URL-detection wxRegEx compile no longer fails — no "Invalid regular expression" in the console and no error dialog. A pre-fix build renders the same text_box, hits IsURL() -> the failing static regex, and fires both signals. Bumps the wxwidgets submodule to the wxConvLibc->UTF-8 emscripten fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
83462f7090
commit
6681e0dd82
1 changed files with 135 additions and 0 deletions
135
tests/kicad/eeschema-url-regex.spec.ts
Normal file
135
tests/kicad/eeschema-url-regex.spec.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import { test, expect } from './fixtures';
|
||||
|
||||
/**
|
||||
* Eeschema URL-detection wxRegEx regression (WASM strconv UTF-8 fix).
|
||||
*
|
||||
* Loading a schematic that renders text/symbol fields calls IsURL() /
|
||||
* LinkifyHTML() (kicad/common/string_utils.cpp), whose static wxRegEx pattern
|
||||
* ends in a negated class containing U+00B6 (PILCROW). In the emscripten build
|
||||
* wxString stores UTF-8 (wxUSE_UNICODE_UTF8) and `wxS(...)` is a NARROW literal;
|
||||
* clang emits U+00B6 as UTF-8 bytes 0xC2 0xB6. The implicit char*->wxString
|
||||
* conversion used wxConvLibc, which under musl's default "C" locale (MB_CUR_MAX
|
||||
* == 1) does NOT decode UTF-8 — it maps each non-ASCII byte into the
|
||||
* surrogate-escape range (0xDF80 + byte), so 0xC2 0xB6 became U+DFC2 U+DFB6.
|
||||
* utf8_str() then re-emitted those as WTF-8 and PCRE2 rejected the pattern with
|
||||
* "Invalid regular expression ...: code points 0xd800-0xdfff are not defined",
|
||||
* popping a modal error dialog over the schematic.
|
||||
*
|
||||
* Fix (wxwidgets/src/common/strconv.cpp): on __EMSCRIPTEN__ bind wxConvLibc to a
|
||||
* UTF-8 converter instead of the libc/locale one, so narrow literals (and all
|
||||
* web I/O, which is UTF-8) decode correctly regardless of the current locale.
|
||||
*
|
||||
* This test opens a text_box-bearing schematic (with a real URL) through the
|
||||
* programmatic Module.kicadOpenFile() hook and asserts the regex error never
|
||||
* fires: no "Invalid regular expression" in the console, no error dialog.
|
||||
*/
|
||||
|
||||
const URL_SCH = `(kicad_sch
|
||||
\t(version 20250114)
|
||||
\t(generator "eeschema")
|
||||
\t(generator_version "9.0")
|
||||
\t(uuid "44444444-4444-4444-4444-444444444444")
|
||||
\t(paper "A4")
|
||||
\t(lib_symbols)
|
||||
\t(text_box "Docs: https://kicad.org/help ¶ end"
|
||||
\t\t(exclude_from_sim no)
|
||||
\t\t(at 50.8 50.8 0)
|
||||
\t\t(size 80 20)
|
||||
\t\t(margins 0.9525 0.9525 0.9525 0.9525)
|
||||
\t\t(stroke (width 0) (type default))
|
||||
\t\t(fill (type none))
|
||||
\t\t(effects (font (size 1.27 1.27)) (justify left top))
|
||||
\t\t(uuid "55555555-0000-0000-0000-000000000001")
|
||||
\t)
|
||||
\t(sheet_instances
|
||||
\t\t(path "/"
|
||||
\t\t\t(page "1")
|
||||
\t\t)
|
||||
\t)
|
||||
)
|
||||
`;
|
||||
|
||||
type EmscriptenFS = {
|
||||
mkdirTree(path: string): void;
|
||||
writeFile(path: string, data: string): void;
|
||||
};
|
||||
type KicadModule = { kicadOpenFile(path: string): unknown };
|
||||
|
||||
const REGEX_ERR = /invalid regular expression|0xd800|UTF-8 error|code points/i;
|
||||
|
||||
test.describe('Eeschema URL-detection regex', () => {
|
||||
test('renders a text_box with a URL without the wxRegEx UTF-8 modal', async ({ page }) => {
|
||||
const consoleLines: string[] = [];
|
||||
page.on('console', (m) => consoleLines.push(`[${m.type()}] ${m.text()}`));
|
||||
|
||||
await page.goto('/kicad/eeschema.html');
|
||||
|
||||
await expect(page.locator('#canvas')).toBeVisible({ timeout: 90000 });
|
||||
await page.waitForFunction(() => !!window.wxElementRegistry, null, { timeout: 90000 });
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
typeof (window as unknown as { Module?: KicadModule }).Module?.kicadOpenFile ===
|
||||
'function',
|
||||
null,
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
!!window.wxElementRegistry &&
|
||||
window.wxElementRegistry
|
||||
.findAll({ visible: true })
|
||||
.some((e) => /Frame$/.test(e.typeName) || (e.name || '').endsWith('Frame')),
|
||||
null,
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
|
||||
expect(await page.title()).toMatch(/untitled/i);
|
||||
|
||||
const openedPath = await page.evaluate((content) => {
|
||||
const w = window as unknown as { FS: EmscriptenFS; Module: KicadModule };
|
||||
const dir = '/home/kicad/documents';
|
||||
try {
|
||||
w.FS.mkdirTree(dir);
|
||||
} catch {
|
||||
/* already exists */
|
||||
}
|
||||
const path = `${dir}/url.kicad_sch`;
|
||||
w.FS.writeFile(path, content);
|
||||
w.Module.kicadOpenFile(path);
|
||||
return path;
|
||||
}, URL_SCH);
|
||||
expect(openedPath).toContain('url.kicad_sch');
|
||||
|
||||
await expect
|
||||
.poll(async () => page.title(), { timeout: 30000, intervals: [500] })
|
||||
.toMatch(/url/i);
|
||||
|
||||
// Let the canvas paint the text_box (this is when IsURL()/LinkifyHTML()
|
||||
// compile the static wxRegEx and would have thrown the modal).
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
await page.screenshot({
|
||||
path: 'test-results/eeschema-url-regex.png',
|
||||
scale: 'device',
|
||||
});
|
||||
|
||||
// The wxRegEx compile failure surfaces two ways: a wxLogError logged to
|
||||
// the console, and (its default GUI target) a modal error dialog. Assert
|
||||
// neither happened. A pre-fix build renders this same text_box and so
|
||||
// hits IsURL() -> the failing static regex -> both signals fire.
|
||||
const regexConsole = consoleLines.filter((l) => REGEX_ERR.test(l));
|
||||
expect(regexConsole, `regex error in console:\n${regexConsole.join('\n')}`).toHaveLength(0);
|
||||
|
||||
const errDialogs = await page.evaluate((src) => {
|
||||
const reg = window.wxElementRegistry;
|
||||
if (!reg) return [];
|
||||
return reg
|
||||
.findAll({})
|
||||
.filter((e: any) =>
|
||||
new RegExp(src, 'i').test(`${e.name || ''} ${e.label || ''} ${e.text || ''}`)
|
||||
)
|
||||
.map((e: any) => ({ typeName: e.typeName, name: e.name, text: e.text }));
|
||||
}, REGEX_ERR.source);
|
||||
expect(errDialogs, `error dialog present:\n${JSON.stringify(errDialogs)}`).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue