Initial commit: Inventory Barcode System

This commit is contained in:
2025-07-22 20:24:51 -04:00
commit 511b01748d
63 changed files with 26932 additions and 0 deletions

View File

@ -0,0 +1,505 @@
/**
* 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 = `
<div class="product-item">
<input type="checkbox" class="product-checkbox" data-product-id="1">
</div>
<div class="product-item">
<input type="checkbox" class="product-checkbox" data-product-id="2">
</div>
`;
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');
});
});
});