fix(links): scope comma handling to parenthesised paths

This commit is contained in:
Devvyn committed 2026-10-29 00:18:20 +08:00
1 parent fd0e52496d
commit 168c15ae08
7 files changed
+70 -12

No files matched your search

+32
View File
@@ -213,6 +213,38 @@ function loadApp(fetchImpl: (url: string, init?: { body?: string }) => unknown)
}
describe('guarded attachment registration results', () => {
it.each(['svg', 'markdown', 'avif', 'bmp', 'ico', 'zip'])(
'names a rejected workspace .%s path without describing it as external',
async (extension) => {
const { app } = loadApp(async () => ({
ok: false,
status: 400,
json: async () => ({ error: 'Unsupported attachment type' }),
}));
const path = `/home/joe/wiki/out/system architecture.${extension}`;
const result = await Object.getPrototypeOf(app)._registerExternalPreview.call(app, path, 's1');
expect(result.status).toBe(400);
expect(result.error).toContain(path);
expect(result.error).not.toContain('outside the session workspace');
}
);
it('retains the external-file explanation for a rejected external type', async () => {
const { app } = loadApp(async () => ({
ok: false,
status: 400,
json: async () => ({ error: 'Unsupported attachment type' }),
}));
const result = await Object.getPrototypeOf(app)._registerExternalPreview.call(
app,
'/mnt/data/system architecture.svg',
's1'
);
expect(result.error).toBe(
'Cannot preview .svg from outside the session workspace (images, video, audio, PDF, Office documents and text files only).'
);
});
it.each([404, 403, 500, 503])('preserves HTTP %s for the activation decision', async (status) => {
const { app, fetch } = loadApp(async () => ({
ok: false,
+8
View File
@@ -46,6 +46,14 @@ describe('spaced file paths', () => {
expect(paths('Updated (/home/me/proj, config.json)')).toEqual(['/home/me/proj', 'config.json']);
});
it.each([
['/mnt/c/Users/me/Smith, John/report.docx', '/mnt/c/Users/me/Smith, John/report.docx'],
['- /home/me/Clients/Acme, Inc/contract.pdf', '/home/me/Clients/Acme, Inc/contract.pdf'],
['Saved to: /home/me/proj/Invoice 12, final.pdf', '/home/me/proj/Invoice 12, final.pdf'],
])('keeps commas in a non-parenthesised spaced path: %s', (line, path) => {
expect(findFilePathLinks(line)).toEqual([{ path, index: line.indexOf(path) }]);
});
it('links an own-line absolute path with spaces whole', () => {
expect(paths(LONG)).toEqual([LONG]);
expect(paths('/mnt/c/proj/My Big Docs/report.docx')).toEqual(['/mnt/c/proj/My Big Docs/report.docx']);
+14 -3
View File
@@ -1,4 +1,4 @@
/** Real xterm clicks must open the complete spaced filename from either wrapped row. */
/** Real xterm clicks resolve the complete filename or report its lookup error from either wrapped row. */
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
@@ -22,12 +22,14 @@ describe('wrapped configuration file links (Chromium)', () => {
{ wrap: 'hard', originalExists: true, extension: 'conf', registrationStatus: 200 },
{ wrap: 'hard', originalExists: true, extension: 'svg', registrationStatus: 400 },
{ wrap: 'hard', originalExists: true, extension: 'markdown', registrationStatus: 400 },
{ wrap: 'hard', originalExists: false, extension: 'svg', registrationStatus: 400 },
])(
'opens the existing .$extension file from both rows: $wrap, original exists: $originalExists',
'handles .$extension links on both rows: $wrap, original exists: $originalExists',
async ({ wrap, originalExists, extension, registrationStatus }) => {
const path = basePath.replace('.conf', `.${extension}`);
const original = path.replace('Kobo eReader', 'KoboeReader');
const expected = wrap === 'hard' && originalExists ? original : path;
const lookupFails = registrationStatus === 400 && !originalExists;
const lookups: string[] = [];
const page = await browser.newPage({ viewport: { width: 1920, height: 600 } });
try {
@@ -106,7 +108,16 @@ describe('wrapped configuration file links (Chromium)', () => {
await page.waitForFunction(() => (window as any).app._linkHovered === true);
await page.mouse.click(point.x, point.y);
await page.waitForFunction((count) => (window as any).opened.length === count, row + 1);
expect(await page.evaluate(() => (window as any).opened)).toEqual(Array(row + 1).fill(expected));
const opened = await page.evaluate(() => (window as any).opened as string[]);
if (lookupFails) {
for (const message of opened) {
expect(message).toMatch(/^error:/);
expect(message).toContain(path);
expect(message).not.toContain('outside the session workspace');
}
} else {
expect(opened).toEqual(Array(row + 1).fill(expected));
}
await page.mouse.move(0, 0);
}
expect(lookups).toEqual(