diff --git a/src/js/logic/add-blank-page.ts b/src/js/logic/add-blank-page.ts
index 6650090..5a695cb 100644
--- a/src/js/logic/add-blank-page.ts
+++ b/src/js/logic/add-blank-page.ts
@@ -6,12 +6,21 @@ import { PDFDocument as PDFLibDocument } from 'pdf-lib';
export async function addBlankPage() {
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
const pageNumberInput = document.getElementById('page-number').value;
+ // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
+ const pageCountInput = document.getElementById('page-count').value;
+
if (pageNumberInput.trim() === '') {
showAlert('Invalid Input', 'Please enter a page number.');
return;
}
+ if (pageCountInput.trim() === '') {
+ showAlert('Invalid Input', 'Please enter the number of pages to insert.');
+ return;
+ }
+
const position = parseInt(pageNumberInput);
+ const pageCount = parseInt(pageCountInput);
const totalPages = state.pdfDoc.getPageCount();
if (isNaN(position) || position < 0 || position > totalPages) {
showAlert(
@@ -21,7 +30,12 @@ export async function addBlankPage() {
return;
}
- showLoader('Adding page...');
+ if (isNaN(pageCount) || pageCount < 1) {
+ showAlert('Invalid Input', 'Please enter a valid number of pages (1 or more).');
+ return;
+ }
+
+ showLoader(`Adding ${pageCount} blank page${pageCount > 1 ? 's' : ''}...`);
try {
const newPdf = await PDFLibDocument.create();
const { width, height } = state.pdfDoc.getPage(0).getSize();
@@ -35,7 +49,10 @@ export async function addBlankPage() {
copied.forEach((p: any) => newPdf.addPage(p));
}
- newPdf.addPage([width, height]);
+ // Add the specified number of blank pages
+ for (let i = 0; i < pageCount; i++) {
+ newPdf.addPage([width, height]);
+ }
if (indicesAfter.length > 0) {
const copied = await newPdf.copyPages(state.pdfDoc, indicesAfter);
@@ -45,11 +62,11 @@ export async function addBlankPage() {
const newPdfBytes = await newPdf.save();
downloadFile(
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
- 'page-added.pdf'
+ `blank-page${pageCount > 1 ? 's' : ''}-added.pdf`
);
} catch (e) {
console.error(e);
- showAlert('Error', 'Could not add a blank page.');
+ showAlert('Error', `Could not add blank page${pageCount > 1 ? 's' : ''}.`);
} finally {
hideLoader();
}
diff --git a/src/js/ui.ts b/src/js/ui.ts
index 557df44..d0e0780 100644
--- a/src/js/ui.ts
+++ b/src/js/ui.ts
@@ -707,15 +707,17 @@ export const toolTemplates = {
`,
'add-blank-page': () => `
-
Add Blank Page
- Insert a new blank page at a specific position in your document.
+ Add Blank Pages
+ Insert one or more blank pages at a specific position in your document.
${createFileInputHTML()}
Total Pages:
-
-
-
+
+
+
+
+
`,
'extract-pages': () => `
diff --git a/src/tests/add-blank-page.test.ts b/src/tests/add-blank-page.test.ts
new file mode 100644
index 0000000..d4400ba
--- /dev/null
+++ b/src/tests/add-blank-page.test.ts
@@ -0,0 +1,348 @@
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { addBlankPage } from '@/js/logic/add-blank-page';
+import * as ui from '@/js/ui';
+import * as helpers from '@/js/utils/helpers';
+import { state } from '@/js/state';
+import { PDFDocument as PDFLibDocument } from 'pdf-lib';
+
+// -------------------- Mock Modules --------------------
+vi.mock('@/js/ui', () => ({
+ showLoader: vi.fn(),
+ hideLoader: vi.fn(),
+ showAlert: vi.fn(),
+}));
+
+vi.mock('@/js/utils/helpers', () => ({
+ downloadFile: vi.fn(),
+}));
+
+vi.mock('pdf-lib', () => ({
+ PDFDocument: {
+ create: vi.fn(),
+ },
+}));
+
+// -------------------- Test Suite --------------------
+describe('Add Blank Page Tool', () => {
+ let mockNewDoc: any;
+
+ beforeEach(() => {
+ // Reset state pdfDoc
+ state.pdfDoc = {
+ getPageCount: () => 5,
+ getPage: vi.fn((index: number) => ({
+ getSize: () => ({ width: 595.28, height: 841.89 }), // A4 size
+ })),
+ } as any;
+
+ // Mock PDFDocument.create
+ mockNewDoc = {
+ copyPages: vi.fn((doc: any, indices: number[]) =>
+ Promise.resolve(indices.map((i: number) => ({ page: `page-${i}` })))
+ ),
+ addPage: vi.fn(),
+ save: vi.fn(() => Promise.resolve(new Uint8Array([1, 2, 3]))),
+ };
+ vi.mocked(PDFLibDocument.create).mockResolvedValue(mockNewDoc);
+
+ // Mock helpers and UI
+ vi.mocked(helpers.downloadFile).mockImplementation(() => {});
+ vi.mocked(ui.showLoader).mockImplementation(() => {});
+ vi.mocked(ui.hideLoader).mockImplementation(() => {});
+ vi.mocked(ui.showAlert).mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ document.body.innerHTML = '';
+ });
+
+ // -------------------- Input Validation Tests --------------------
+ describe('Input Validation', () => {
+ it('should show alert for empty page number', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter a page number.'
+ );
+ expect(ui.showLoader).not.toHaveBeenCalled();
+ });
+
+ it('should show alert for empty page count', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter the number of pages to insert.'
+ );
+ expect(ui.showLoader).not.toHaveBeenCalled();
+ });
+
+ it('should show alert for invalid page number (negative)', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter a number between 0 and 5.'
+ );
+ });
+
+ it('should show alert for invalid page number (too high)', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter a number between 0 and 5.'
+ );
+ });
+
+ it('should show alert for invalid page count (zero)', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter a valid number of pages (1 or more).'
+ );
+ });
+
+ it('should show alert for invalid page count (negative)', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith(
+ 'Invalid Input',
+ 'Please enter a valid number of pages (1 or more).'
+ );
+ });
+ });
+
+ // -------------------- Single Page Insertion Tests --------------------
+ describe('Single Page Insertion', () => {
+ it('should add one blank page at the beginning', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 1 blank page...');
+ expect(PDFLibDocument.create).toHaveBeenCalled();
+ // Should add 1 blank page + 5 existing pages = 6 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(6);
+ expect(mockNewDoc.addPage).toHaveBeenCalledWith([595.28, 841.89]);
+ expect(mockNewDoc.save).toHaveBeenCalled();
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-page-added.pdf'
+ );
+ expect(ui.hideLoader).toHaveBeenCalled();
+ });
+
+ it('should add one blank page in the middle', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 1 blank page...');
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [0, 1]);
+ // Should add 1 blank page + 5 existing pages = 6 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(6);
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [2, 3, 4]);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-page-added.pdf'
+ );
+ });
+
+ it('should add one blank page at the end', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [0, 1, 2, 3, 4]);
+ // Should add 1 blank page + 5 existing pages = 6 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(6);
+ // When adding at the end, there are no pages after, so copyPages is not called for indicesAfter
+ expect(mockNewDoc.copyPages).toHaveBeenCalledTimes(1);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-page-added.pdf'
+ );
+ });
+ });
+
+ // -------------------- Multiple Pages Insertion Tests --------------------
+ describe('Multiple Pages Insertion', () => {
+ it('should add multiple blank pages at the beginning', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 3 blank pages...');
+ // Should add 3 blank pages + 5 existing pages = 8 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(8);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-pages-added.pdf'
+ );
+ });
+
+ it('should add multiple blank pages in the middle', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 5 blank pages...');
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [0, 1]);
+ // Should add 5 blank pages + 5 existing pages = 10 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(10);
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [2, 3, 4]);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-pages-added.pdf'
+ );
+ });
+
+ it('should add multiple blank pages at the end', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 2 blank pages...');
+ expect(mockNewDoc.copyPages).toHaveBeenCalledWith(state.pdfDoc, [0, 1, 2, 3, 4]);
+ // Should add 2 blank pages + 5 existing pages = 7 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(7);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-pages-added.pdf'
+ );
+ });
+ });
+
+ // -------------------- Error Handling Tests --------------------
+ describe('Error Handling', () => {
+ it('should handle PDF creation errors', async () => {
+ vi.mocked(PDFLibDocument.create).mockRejectedValue(new Error('PDF creation failed'));
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith('Error', 'Could not add blank page.');
+ expect(ui.hideLoader).toHaveBeenCalled();
+ });
+
+ it('should handle PDF processing errors', async () => {
+ mockNewDoc.copyPages.mockRejectedValue(new Error('Copy failed'));
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith('Error', 'Could not add blank page.');
+ expect(ui.hideLoader).toHaveBeenCalled();
+ });
+
+ it('should handle save errors', async () => {
+ mockNewDoc.save.mockRejectedValue(new Error('Save failed'));
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showAlert).toHaveBeenCalledWith('Error', 'Could not add blank page.');
+ expect(ui.hideLoader).toHaveBeenCalled();
+ });
+ });
+
+ // -------------------- Edge Cases Tests --------------------
+ describe('Edge Cases', () => {
+ it('should handle empty PDF (0 pages)', async () => {
+ state.pdfDoc.getPageCount = () => 0;
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ // When PDF has 0 pages, copyPages is not called at all
+ expect(mockNewDoc.copyPages).not.toHaveBeenCalled();
+ // Should add 1 blank page + 0 existing pages = 1 total call
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(1);
+ });
+
+ it('should handle large number of pages', async () => {
+ document.body.innerHTML = `
+
+
+ `;
+
+ await addBlankPage();
+
+ expect(ui.showLoader).toHaveBeenCalledWith('Adding 100 blank pages...');
+ // Should add 100 blank pages + 5 existing pages = 105 total calls
+ expect(mockNewDoc.addPage).toHaveBeenCalledTimes(105);
+ expect(helpers.downloadFile).toHaveBeenCalledWith(
+ expect.any(Blob),
+ 'blank-pages-added.pdf'
+ );
+ });
+ });
+});