/** * Frontend Barcode Generation Interface Tests * Tests the barcode generation UI functionality including product selection, options, and preview */ // Mock DOM environment for testing const { JSDOM } = require('jsdom'); const fs = require('fs'); const path = require('path'); describe('Barcode Generation Interface', () => { let dom; let document; let window; let InventoryApp; beforeEach(() => { // Load the HTML file const htmlPath = path.join(__dirname, '../public/index.html'); const htmlContent = fs.readFileSync(htmlPath, 'utf8'); // Create JSDOM instance dom = new JSDOM(htmlContent, { url: 'http://localhost:3000', pretendToBeVisual: true, resources: 'usable' }); document = dom.window.document; window = dom.window; // Mock global objects global.document = document; global.window = window; global.FormData = window.FormData; global.fetch = jest.fn(); // Mock file API global.File = class File { constructor(parts, filename, options = {}) { this.name = filename; this.size = parts.reduce((size, part) => size + part.length, 0); this.type = options.type || ''; } }; // Load the JavaScript application const jsPath = path.join(__dirname, '../public/js/app.js'); let jsContent = fs.readFileSync(jsPath, 'utf8'); // Modify the JS content to expose InventoryApp class jsContent = jsContent.replace( 'class InventoryApp {', 'window.InventoryApp = class InventoryApp {' ); // Execute the JavaScript in the JSDOM context const script = new window.Function(jsContent); script.call(window); // Get the InventoryApp class from the window context InventoryApp = window.InventoryApp; }); afterEach(() => { jest.clearAllMocks(); dom.window.close(); }); describe('Initialization', () => { test('should initialize barcode generation state correctly', () => { const app = new InventoryApp(); expect(app.products).toEqual([]); expect(app.selectedProducts).toEqual([]); expect(app.generationOptions).toEqual({ codeType: 'barcode', barcodeFormat: 'CODE128', includeText: 'code', labelSize: 'medium', labelsPerPage: 6, copiesPerProduct: 1 }); }); test('should set up barcode generation event listeners', () => { const app = new InventoryApp(); // Check that key elements exist expect(document.getElementById('load-products')).toBeTruthy(); expect(document.getElementById('product-search')).toBeTruthy(); expect(document.getElementById('select-all-products')).toBeTruthy(); expect(document.getElementById('generate-codes')).toBeTruthy(); }); test('should initialize with generate codes button disabled', () => { new InventoryApp(); const generateButton = document.getElementById('generate-codes'); expect(generateButton.disabled).toBe(true); }); }); describe('Product Loading', () => { test('should load products from server successfully', async () => { const app = new InventoryApp(); const mockProducts = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Widget B', quantity: 25 } ]; global.fetch.mockResolvedValueOnce({ ok: true, json: async () => mockProducts }); await app.loadProducts(); expect(app.products).toEqual(mockProducts); expect(document.getElementById('product-list-container').style.display).toBe('block'); }); test('should show mock products when server is unavailable', async () => { const app = new InventoryApp(); global.fetch.mockRejectedValueOnce(new Error('Network error')); await app.loadProducts(); expect(app.products.length).toBeGreaterThan(0); expect(app.products[0]).toHaveProperty('product_code'); expect(app.products[0]).toHaveProperty('description'); }); test('should display products in the product list', () => { const app = new InventoryApp(); const mockProducts = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Widget B', quantity: 25 } ]; app.displayProducts(mockProducts); const productList = document.getElementById('product-list'); expect(productList.children.length).toBe(2); // Check first product display const firstProduct = productList.children[0]; expect(firstProduct.querySelector('.product-code').textContent).toBe('ABC123'); expect(firstProduct.querySelector('.product-description').textContent).toBe('Widget A'); }); test('should show no products message when list is empty', () => { const app = new InventoryApp(); app.displayProducts([]); const productList = document.getElementById('product-list'); expect(productList.textContent).toContain('No products found'); }); }); describe('Product Selection', () => { test('should select and deselect products correctly', () => { const app = new InventoryApp(); app.toggleProductSelection(1, true); expect(app.selectedProducts).toContain(1); app.toggleProductSelection(1, false); expect(app.selectedProducts).not.toContain(1); }); test('should update selected count when products are selected', () => { const app = new InventoryApp(); app.toggleProductSelection(1, true); app.toggleProductSelection(2, true); expect(document.getElementById('selected-count').textContent).toBe('2 selected'); }); test('should enable generate button when products are selected', () => { const app = new InventoryApp(); app.toggleProductSelection(1, true); const generateButton = document.getElementById('generate-codes'); expect(generateButton.disabled).toBe(false); }); test('should select all products when select all is checked', () => { const app = new InventoryApp(); // Mock some products in the DOM const productList = document.getElementById('product-list'); productList.innerHTML = `
`; app.toggleSelectAllProducts(true); expect(app.selectedProducts).toContain(1); expect(app.selectedProducts).toContain(2); }); }); describe('Product Filtering', () => { test('should filter products by product code', () => { const app = new InventoryApp(); app.displayProducts = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Widget B', quantity: 25 }, { id: 3, product_code: 'ABC789', description: 'Widget C', quantity: 75 } ]; app.filterProducts('ABC'); expect(app.displayProducts).toHaveBeenCalledWith([ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 }, { id: 3, product_code: 'ABC789', description: 'Widget C', quantity: 75 } ]); }); test('should filter products by description', () => { const app = new InventoryApp(); app.displayProducts = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Red Widget', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Blue Widget', quantity: 25 }, { id: 3, product_code: 'GHI789', description: 'Green Tool', quantity: 75 } ]; app.filterProducts('Widget'); expect(app.displayProducts).toHaveBeenCalledWith([ { id: 1, product_code: 'ABC123', description: 'Red Widget', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Blue Widget', quantity: 25 } ]); }); }); describe('Generation Options', () => { test('should update generation options when form values change', () => { const app = new InventoryApp(); // Simulate changing code type document.getElementById('code-type').value = 'qrcode'; document.getElementById('code-type').dispatchEvent(new window.Event('change')); expect(app.generationOptions.codeType).toBe('qrcode'); }); test('should hide barcode format when QR code is selected', () => { const app = new InventoryApp(); app.generationOptions.codeType = 'qrcode'; app.toggleBarcodeFormatVisibility(); const barcodeFormatGroup = document.getElementById('barcode-format-group'); expect(barcodeFormatGroup.style.display).toBe('none'); }); test('should show barcode format when barcode is selected', () => { const app = new InventoryApp(); app.generationOptions.codeType = 'barcode'; app.toggleBarcodeFormatVisibility(); const barcodeFormatGroup = document.getElementById('barcode-format-group'); expect(barcodeFormatGroup.style.display).toBe('flex'); }); test('should show custom size inputs when custom label size is selected', () => { const app = new InventoryApp(); app.generationOptions.labelSize = 'custom'; app.toggleCustomSizeVisibility(); const customSizeGroup = document.getElementById('custom-size-group'); expect(customSizeGroup.style.display).toBe('block'); }); }); describe('Preview Generation', () => { test('should generate preview for selected products', async () => { const app = new InventoryApp(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 }, { id: 2, product_code: 'DEF456', description: 'Widget B', quantity: 25 } ]; app.selectedProducts = [1, 2]; await app.generatePreview(); const previewSection = document.getElementById('preview-section'); expect(previewSection.style.display).toBe('block'); const downloadButton = document.getElementById('download-pdf'); expect(downloadButton.disabled).toBe(false); }); test('should show error when no products are selected for preview', async () => { const app = new InventoryApp(); app.showError = jest.fn(); app.selectedProducts = []; await app.generatePreview(); expect(app.showError).toHaveBeenCalledWith('Please select at least one product to generate preview'); }); test('should display code preview with correct content', () => { const app = new InventoryApp(); const mockProducts = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 } ]; app.displayCodePreview(mockProducts); const previewArea = document.getElementById('preview-area'); expect(previewArea.classList.contains('has-content')).toBe(true); expect(previewArea.innerHTML).toContain('Preview (1 products)'); }); test('should generate correct text content based on options', () => { const app = new InventoryApp(); const product = { product_code: 'ABC123', description: 'Widget A' }; expect(app.generateTextContent(product, 'code')).toContain('ABC123'); expect(app.generateTextContent(product, 'description')).toContain('Widget A'); expect(app.generateTextContent(product, 'both')).toContain('ABC123'); expect(app.generateTextContent(product, 'both')).toContain('Widget A'); expect(app.generateTextContent(product, 'none')).toBe(''); }); }); describe('Code Generation', () => { test('should generate codes successfully', async () => { const app = new InventoryApp(); app.showSuccess = jest.fn(); app.generatePreview = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 } ]; app.selectedProducts = [1]; global.fetch.mockResolvedValueOnce({ ok: true, json: async () => ({ count: 1 }) }); await app.generateCodes(); expect(app.showSuccess).toHaveBeenCalledWith('Successfully generated codes for 1 products!'); expect(app.generatePreview).toHaveBeenCalled(); }); test('should handle code generation failure gracefully', async () => { const app = new InventoryApp(); app.showSuccess = jest.fn(); app.generatePreview = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 } ]; app.selectedProducts = [1]; global.fetch.mockRejectedValueOnce(new Error('Network error')); await app.generateCodes(); expect(app.showSuccess).toHaveBeenCalledWith('Successfully generated codes for 1 products!'); expect(app.generatePreview).toHaveBeenCalled(); }); test('should show error when no products selected for generation', async () => { const app = new InventoryApp(); app.showError = jest.fn(); app.selectedProducts = []; await app.generateCodes(); expect(app.showError).toHaveBeenCalledWith('Please select at least one product to generate codes'); }); }); describe('PDF Download', () => { test('should download PDF successfully', async () => { const app = new InventoryApp(); app.showSuccess = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 } ]; app.selectedProducts = [1]; // Mock blob response const mockBlob = new window.Blob(['pdf content'], { type: 'application/pdf' }); global.fetch.mockResolvedValueOnce({ ok: true, blob: async () => mockBlob }); // Mock URL.createObjectURL window.URL.createObjectURL = jest.fn(() => 'blob:url'); window.URL.revokeObjectURL = jest.fn(); await app.downloadPDF(); expect(app.showSuccess).toHaveBeenCalledWith('PDF downloaded successfully!'); }); test('should handle PDF download failure gracefully', async () => { const app = new InventoryApp(); app.showSuccess = jest.fn(); app.products = [ { id: 1, product_code: 'ABC123', description: 'Widget A', quantity: 50 } ]; app.selectedProducts = [1]; global.fetch.mockRejectedValueOnce(new Error('Network error')); await app.downloadPDF(); expect(app.showSuccess).toHaveBeenCalledWith('PDF would be downloaded in a real implementation!'); }); }); describe('State Management', () => { test('should reset generation state correctly', () => { const app = new InventoryApp(); // Set some state app.selectedProducts = [1, 2, 3]; app.generationOptions.codeType = 'qrcode'; // Show some UI elements document.getElementById('product-list-container').style.display = 'block'; document.getElementById('preview-section').style.display = 'block'; app.resetGenerationState(); expect(app.selectedProducts).toEqual([]); expect(app.generationOptions.codeType).toBe('barcode'); expect(document.getElementById('product-list-container').style.display).toBe('none'); expect(document.getElementById('preview-section').style.display).toBe('none'); }); test('should reset form values when resetting state', () => { const app = new InventoryApp(); // Change form values document.getElementById('code-type').value = 'qrcode'; document.getElementById('product-search').value = 'test search'; app.resetGenerationState(); expect(document.getElementById('code-type').value).toBe('barcode'); expect(document.getElementById('product-search').value).toBe(''); }); }); describe('UI Interactions', () => { test('should switch to generate tab correctly', () => { const app = new InventoryApp(); app.switchTab('generate'); expect(app.currentTab).toBe('generate'); const activeTab = document.querySelector('.nav-tab.active'); expect(activeTab.dataset.tab).toBe('generate'); const activeContent = document.querySelector('.tab-content.active'); expect(activeContent.id).toBe('generate-tab'); }); test('should handle generate tab click', () => { const app = new InventoryApp(); const generateTab = document.querySelector('[data-tab="generate"]'); generateTab.click(); expect(app.currentTab).toBe('generate'); }); }); });