From 90fe106664d5b5f5310d678fa0864fca04c182fa Mon Sep 17 00:00:00 2001 From: nipunrajk Date: Wed, 2 Sep 2026 22:06:16 +0530 Subject: [PATCH 1/3] fix(navigation): respect BASE_URL in tool pages and navbar brand (Fixes #850) --- simple-index.html | 2 +- src/js/logic/deskew-pdf-page.ts | 2 +- src/js/logic/form-filler-page.ts | 2 +- src/js/logic/markdown-to-pdf-page.ts | 26 +++---- src/js/logic/remove-annotations-page.ts | 2 +- src/js/logic/remove-blank-pages-page.ts | 2 +- src/tests/navigation-base-url.test.ts | 95 +++++++++++++++++++++++++ 7 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/tests/navigation-base-url.test.ts diff --git a/simple-index.html b/simple-index.html index e74e1434..cd6140b6 100644 --- a/simple-index.html +++ b/simple-index.html @@ -60,7 +60,7 @@ class="h-8 w-8" /> - {{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}} diff --git a/src/js/logic/deskew-pdf-page.ts b/src/js/logic/deskew-pdf-page.ts index 0428d704..f5467770 100644 --- a/src/js/logic/deskew-pdf-page.ts +++ b/src/js/logic/deskew-pdf-page.ts @@ -273,7 +273,7 @@ function initPage(): void { if (backBtn) { backBtn.addEventListener('click', () => { - window.location.href = '/'; + window.location.href = import.meta.env.BASE_URL; }); } diff --git a/src/js/logic/form-filler-page.ts b/src/js/logic/form-filler-page.ts index ae49b487..47772bdc 100644 --- a/src/js/logic/form-filler-page.ts +++ b/src/js/logic/form-filler-page.ts @@ -311,6 +311,6 @@ document.addEventListener('DOMContentLoaded', () => { processBtn?.addEventListener('click', processAndDownloadForm); backBtn?.addEventListener('click', () => { - window.location.href = '../../index.html'; + window.location.href = import.meta.env.BASE_URL; }); }); diff --git a/src/js/logic/markdown-to-pdf-page.ts b/src/js/logic/markdown-to-pdf-page.ts index e104c983..3282f4cd 100644 --- a/src/js/logic/markdown-to-pdf-page.ts +++ b/src/js/logic/markdown-to-pdf-page.ts @@ -1,21 +1,21 @@ import { MarkdownEditor } from '../utils/markdown-editor.js'; document.addEventListener('DOMContentLoaded', () => { - const container = document.getElementById('markdown-editor-container'); + const container = document.getElementById('markdown-editor-container'); - if (!container) { - console.error('Markdown editor container not found'); - return; - } + if (!container) { + console.error('Markdown editor container not found'); + return; + } - const editor = new MarkdownEditor(container, {}); + const editor = new MarkdownEditor(container, {}); - console.log('Markdown editor initialized'); + console.log('Markdown editor initialized'); - const backButton = document.getElementById('back-to-tools'); - if (backButton) { - backButton.addEventListener('click', () => { - window.location.href = '/'; - }); - } + const backButton = document.getElementById('back-to-tools'); + if (backButton) { + backButton.addEventListener('click', () => { + window.location.href = import.meta.env.BASE_URL; + }); + } }); diff --git a/src/js/logic/remove-annotations-page.ts b/src/js/logic/remove-annotations-page.ts index bea92451..0ff3fda0 100644 --- a/src/js/logic/remove-annotations-page.ts +++ b/src/js/logic/remove-annotations-page.ts @@ -189,6 +189,6 @@ document.addEventListener('DOMContentLoaded', () => { processBtn?.addEventListener('click', processRemoveAnnotations); backBtn?.addEventListener('click', () => { - window.location.href = '../../index.html'; + window.location.href = import.meta.env.BASE_URL; }); }); diff --git a/src/js/logic/remove-blank-pages-page.ts b/src/js/logic/remove-blank-pages-page.ts index 5b70b371..540e37c6 100644 --- a/src/js/logic/remove-blank-pages-page.ts +++ b/src/js/logic/remove-blank-pages-page.ts @@ -366,6 +366,6 @@ document.addEventListener('DOMContentLoaded', () => { processBtn?.addEventListener('click', processRemoveBlankPages); document.getElementById('back-to-tools')?.addEventListener('click', () => { - window.location.href = '../../index.html'; + window.location.href = import.meta.env.BASE_URL; }); }); diff --git a/src/tests/navigation-base-url.test.ts b/src/tests/navigation-base-url.test.ts new file mode 100644 index 00000000..282e3559 --- /dev/null +++ b/src/tests/navigation-base-url.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect } from 'vitest'; +import * as fs from 'fs'; +import * as path from 'path'; + +describe('Navigation BASE_URL Consistency', () => { + const logicDir = path.resolve(__dirname, '../js/logic'); + const pagesDir = path.resolve(__dirname, '../pages'); + const rootDir = path.resolve(__dirname, '../../'); + + it('ensures no tool logic files hardcode root "/" or relative paths for navigation', () => { + const files = fs + .readdirSync(logicDir) + .filter((file) => file.endsWith('.ts') || file.endsWith('.js')); + + const offendingFiles: string[] = []; + + for (const file of files) { + const content = fs.readFileSync(path.join(logicDir, file), 'utf-8'); + + // Check for hardcoded root redirects or relative path escapes + if ( + /window\.location\.href\s*=\s*['"`]\/['"`]/.test(content) || + /window\.location\.href\s*=\s*['"`]\.\.\//.test(content) + ) { + offendingFiles.push(file); + } + } + + expect( + offendingFiles, + `The following files hardcode root or relative navigation instead of import.meta.env.BASE_URL: ${offendingFiles.join( + ', ' + )}` + ).toEqual([]); + }); + + it('ensures tool logic files with back-to-tools button use import.meta.env.BASE_URL', () => { + const files = fs + .readdirSync(logicDir) + .filter((file) => file.endsWith('.ts') || file.endsWith('.js')); + + const filesMissingBaseUrl: string[] = []; + + for (const file of files) { + const content = fs.readFileSync(path.join(logicDir, file), 'utf-8'); + + // If the file handles 'back-to-tools' click navigation + if ( + content.includes('back-to-tools') && + content.includes('window.location.href') && + !content.includes('import.meta.env.BASE_URL') + ) { + filesMissingBaseUrl.push(file); + } + } + + expect( + filesMissingBaseUrl, + `The following files handle back-to-tools navigation without import.meta.env.BASE_URL: ${filesMissingBaseUrl.join( + ', ' + )}` + ).toEqual([]); + }); + + it('ensures HTML templates do not hardcode href="/" for brand links', () => { + const htmlFiles = fs + .readdirSync(pagesDir) + .filter((file) => file.endsWith('.html')) + .map((file) => path.join(pagesDir, file)); + + const simpleIndex = path.join(rootDir, 'simple-index.html'); + if (fs.existsSync(simpleIndex)) { + htmlFiles.push(simpleIndex); + } + + const offendingFiles: string[] = []; + + for (const filePath of htmlFiles) { + const content = fs.readFileSync(filePath, 'utf-8'); + + // Brand link / logo anchor should not be hardcoded to href="/" + // e.g. should be + if (/]*href=["']\/["'][^>]*>.*BentoPDF/i.test(content)) { + offendingFiles.push(path.basename(filePath)); + } + } + + expect( + offendingFiles, + `The following HTML templates hardcode href="/" for the brand link: ${offendingFiles.join( + ', ' + )}` + ).toEqual([]); + }); +}); From 060d57639e4da1270099bff164be0e1fdc4fedcc Mon Sep 17 00:00:00 2001 From: nipunrajk Date: Wed, 2 Sep 2026 22:22:35 +0530 Subject: [PATCH 2/3] test(navigation): tighten BASE_URL assertions per code review --- src/tests/navigation-base-url.test.ts | 113 ++++++++++++-------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/src/tests/navigation-base-url.test.ts b/src/tests/navigation-base-url.test.ts index 282e3559..f41c61c2 100644 --- a/src/tests/navigation-base-url.test.ts +++ b/src/tests/navigation-base-url.test.ts @@ -7,89 +7,84 @@ describe('Navigation BASE_URL Consistency', () => { const pagesDir = path.resolve(__dirname, '../pages'); const rootDir = path.resolve(__dirname, '../../'); - it('ensures no tool logic files hardcode root "/" or relative paths for navigation', () => { + it('ensures tool navigation assignments strictly use import.meta.env.BASE_URL', () => { const files = fs .readdirSync(logicDir) - .filter((file) => file.endsWith('.ts') || file.endsWith('.js')); + .filter( + (file) => + (file.endsWith('.ts') || file.endsWith('.js')) && + file !== 'shortcuts.ts' + ); - const offendingFiles: string[] = []; + const nonCompliantAssignments: Array<{ file: string; target: string }> = []; for (const file of files) { const content = fs.readFileSync(path.join(logicDir, file), 'utf-8'); + const assignments = content.matchAll( + /window\.location\.href\s*=\s*([^;\n]+)/g + ); - // Check for hardcoded root redirects or relative path escapes - if ( - /window\.location\.href\s*=\s*['"`]\/['"`]/.test(content) || - /window\.location\.href\s*=\s*['"`]\.\.\//.test(content) - ) { - offendingFiles.push(file); + for (const match of assignments) { + const target = match[1].trim(); + if (!target.includes('import.meta.env.BASE_URL')) { + nonCompliantAssignments.push({ file, target }); + } } } expect( - offendingFiles, - `The following files hardcode root or relative navigation instead of import.meta.env.BASE_URL: ${offendingFiles.join( - ', ' + nonCompliantAssignments, + `The following files assign window.location.href without import.meta.env.BASE_URL: ${JSON.stringify( + nonCompliantAssignments, + null, + 2 )}` ).toEqual([]); }); - it('ensures tool logic files with back-to-tools button use import.meta.env.BASE_URL', () => { - const files = fs - .readdirSync(logicDir) - .filter((file) => file.endsWith('.ts') || file.endsWith('.js')); + it('ensures each affected back-button handler assigns import.meta.env.BASE_URL', () => { + const affectedFiles = [ + 'deskew-pdf-page.ts', + 'markdown-to-pdf-page.ts', + 'remove-annotations-page.ts', + 'form-filler-page.ts', + 'remove-blank-pages-page.ts', + ]; - const filesMissingBaseUrl: string[] = []; - - for (const file of files) { + for (const file of affectedFiles) { const content = fs.readFileSync(path.join(logicDir, file), 'utf-8'); - - // If the file handles 'back-to-tools' click navigation - if ( - content.includes('back-to-tools') && - content.includes('window.location.href') && - !content.includes('import.meta.env.BASE_URL') - ) { - filesMissingBaseUrl.push(file); - } + expect( + content, + `${file} must assign window.location.href to import.meta.env.BASE_URL` + ).toMatch(/window\.location\.href\s*=\s*import\.meta\.env\.BASE_URL/); } - - expect( - filesMissingBaseUrl, - `The following files handle back-to-tools navigation without import.meta.env.BASE_URL: ${filesMissingBaseUrl.join( - ', ' - )}` - ).toEqual([]); }); - it('ensures HTML templates do not hardcode href="/" for brand links', () => { - const htmlFiles = fs - .readdirSync(pagesDir) - .filter((file) => file.endsWith('.html')) - .map((file) => path.join(pagesDir, file)); + it('ensures brand anchor in standalone templates asserts href="{{baseUrl}}"', () => { + const standaloneTemplates = [ + path.join(pagesDir, 'pdf-multi-tool.html'), + path.join(rootDir, 'simple-index.html'), + path.join(rootDir, 'src/partials/navbar-simple.html'), + ]; - const simpleIndex = path.join(rootDir, 'simple-index.html'); - if (fs.existsSync(simpleIndex)) { - htmlFiles.push(simpleIndex); - } + for (const filePath of standaloneTemplates) { + if (!fs.existsSync(filePath)) continue; - const offendingFiles: string[] = []; - - for (const filePath of htmlFiles) { const content = fs.readFileSync(filePath, 'utf-8'); + const brandAnchorMatch = content.match( + /]*href=["']([^"']*)["'][^>]*>[\s\S]*?(?:{{#if\s+brandName}}|BentoPDF)/i + ); - // Brand link / logo anchor should not be hardcoded to href="/" - // e.g. should be - if (/]*href=["']\/["'][^>]*>.*BentoPDF/i.test(content)) { - offendingFiles.push(path.basename(filePath)); - } + expect( + brandAnchorMatch, + `Could not locate brand anchor in ${path.basename(filePath)}` + ).not.toBeNull(); + + const href = brandAnchorMatch?.[1]; + expect( + href, + `Expected ${path.basename(filePath)} brand anchor href to be "{{baseUrl}}" but got "${href}"` + ).toBe('{{baseUrl}}'); } - - expect( - offendingFiles, - `The following HTML templates hardcode href="/" for the brand link: ${offendingFiles.join( - ', ' - )}` - ).toEqual([]); }); }); From daacdd2a2eeb07bd789e7a4ffa607fe0e7164678 Mon Sep 17 00:00:00 2001 From: nipunrajk Date: Wed, 2 Sep 2026 22:31:56 +0530 Subject: [PATCH 3/3] test(navigation): constrain brand anchor regex to individual elements --- src/tests/navigation-base-url.test.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/tests/navigation-base-url.test.ts b/src/tests/navigation-base-url.test.ts index f41c61c2..a5e7ecc5 100644 --- a/src/tests/navigation-base-url.test.ts +++ b/src/tests/navigation-base-url.test.ts @@ -62,7 +62,6 @@ describe('Navigation BASE_URL Consistency', () => { it('ensures brand anchor in standalone templates asserts href="{{baseUrl}}"', () => { const standaloneTemplates = [ - path.join(pagesDir, 'pdf-multi-tool.html'), path.join(rootDir, 'simple-index.html'), path.join(rootDir, 'src/partials/navbar-simple.html'), ]; @@ -71,20 +70,32 @@ describe('Navigation BASE_URL Consistency', () => { if (!fs.existsSync(filePath)) continue; const content = fs.readFileSync(filePath, 'utf-8'); - const brandAnchorMatch = content.match( - /]*href=["']([^"']*)["'][^>]*>[\s\S]*?(?:{{#if\s+brandName}}|BentoPDF)/i + const anchors = [ + ...content.matchAll( + /]*href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a\s*>/gi + ), + ]; + + const brandAnchor = anchors.find(([, , innerContent]) => + /{{#if\s+brandName}}|BentoPDF/i.test(innerContent) ); expect( - brandAnchorMatch, + brandAnchor, `Could not locate brand anchor in ${path.basename(filePath)}` - ).not.toBeNull(); + ).toBeDefined(); - const href = brandAnchorMatch?.[1]; + const href = brandAnchor?.[1]; expect( href, `Expected ${path.basename(filePath)} brand anchor href to be "{{baseUrl}}" but got "${href}"` ).toBe('{{baseUrl}}'); } }); + + it('ensures pdf-multi-tool uses standard navbar partial', () => { + const pdfMultiTool = path.join(pagesDir, 'pdf-multi-tool.html'); + const content = fs.readFileSync(pdfMultiTool, 'utf-8'); + expect(content).toContain('{{> navbar }}'); + }); });