fix: queue issue
This commit is contained in:
@@ -5,6 +5,26 @@
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// Load the real queue-api.js module
|
||||
function loadQueueAPI() {
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const src = fs.readFileSync(
|
||||
path.resolve(__dirname, '../../../src/server/web/static/js/queue/queue-api.js'),
|
||||
'utf8'
|
||||
);
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(src);
|
||||
return global.AniWorld.QueueAPI;
|
||||
}
|
||||
|
||||
// Stub the minimal dependencies queue-api.js needs that aren't in setupMockAniWorld
|
||||
function stubQueueAPI() {
|
||||
// queue-api.js calls AniWorld.Constants.API.* — those are already in setupMockAniWorld
|
||||
// AniWorld.ApiClient is already a vi.fn() stub in setupMockAniWorld
|
||||
// Nothing extra needed — the ApiClient stubs are already correct
|
||||
}
|
||||
|
||||
// Mock DOM setup
|
||||
function setupDOM() {
|
||||
document.body.innerHTML = `
|
||||
@@ -93,24 +113,36 @@ function setupMockAniWorld() {
|
||||
ProgressHandler: {
|
||||
processPendingProgressUpdates: vi.fn(),
|
||||
updateProgress: vi.fn()
|
||||
},
|
||||
QueueAPI: {
|
||||
loadQueueData: vi.fn(),
|
||||
startQueue: vi.fn(),
|
||||
stopQueue: vi.fn(),
|
||||
removeFromQueue: vi.fn(),
|
||||
retryDownloads: vi.fn(),
|
||||
clearCompleted: vi.fn(),
|
||||
clearFailed: vi.fn(),
|
||||
clearPending: vi.fn()
|
||||
}
|
||||
// QueueAPI intentionally omitted — tests that need it call loadQueueAPI()
|
||||
// to get the real module; inline handlers in button tests need the mock to
|
||||
// delegate, so we patch it after setupMockAniWorld in those describe blocks.
|
||||
};
|
||||
}
|
||||
|
||||
// Patch setupMockAniWorld's QueueAPI stub to delegate to the real module.
|
||||
// Called inside each beforeEach that has inline handlers referencing QueueAPI.
|
||||
function patchQueueAPIDelegate() {
|
||||
const real = loadQueueAPI();
|
||||
global.AniWorld.QueueAPI = {
|
||||
loadQueueData: real.loadQueueData,
|
||||
startQueue: real.startQueue,
|
||||
stopQueue: real.stopQueue,
|
||||
removeFromQueue: real.removeFromQueue,
|
||||
retryDownloads: real.retryDownloads,
|
||||
clearCompleted: real.clearCompleted,
|
||||
clearFailed: real.clearFailed,
|
||||
clearPending: real.clearPending,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Queue API - Data Loading', () => {
|
||||
let QueueAPI;
|
||||
|
||||
beforeEach(() => {
|
||||
setupDOM();
|
||||
setupMockAniWorld();
|
||||
QueueAPI = loadQueueAPI();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -141,7 +173,7 @@ describe('Queue API - Data Loading', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.get.mockResolvedValue(mockResponse);
|
||||
|
||||
const data = await global.AniWorld.QueueAPI.loadQueueData();
|
||||
const data = await QueueAPI.loadQueueData();
|
||||
|
||||
expect(global.AniWorld.ApiClient.get).toHaveBeenCalledWith('/api/queue/status');
|
||||
expect(data).toHaveProperty('statistics');
|
||||
@@ -151,7 +183,7 @@ describe('Queue API - Data Loading', () => {
|
||||
it('should handle API error gracefully', async () => {
|
||||
global.AniWorld.ApiClient.get.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const data = await global.AniWorld.QueueAPI.loadQueueData();
|
||||
const data = await QueueAPI.loadQueueData();
|
||||
|
||||
expect(data).toBeNull();
|
||||
});
|
||||
@@ -176,7 +208,7 @@ describe('Queue API - Data Loading', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.get.mockResolvedValue(mockResponse);
|
||||
|
||||
const data = await global.AniWorld.QueueAPI.loadQueueData();
|
||||
const data = await QueueAPI.loadQueueData();
|
||||
|
||||
expect(data.is_running).toBe(true);
|
||||
expect(data.pending_items).toHaveLength(1);
|
||||
@@ -185,9 +217,12 @@ describe('Queue API - Data Loading', () => {
|
||||
});
|
||||
|
||||
describe('Queue API - Queue Control', () => {
|
||||
let QueueAPI;
|
||||
|
||||
beforeEach(() => {
|
||||
setupDOM();
|
||||
setupMockAniWorld();
|
||||
QueueAPI = loadQueueAPI();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -200,7 +235,7 @@ describe('Queue API - Queue Control', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.post.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.startQueue();
|
||||
const result = await QueueAPI.startQueue();
|
||||
|
||||
expect(global.AniWorld.ApiClient.post).toHaveBeenCalledWith('/api/queue/start', {});
|
||||
expect(result.message).toBe('Queue started');
|
||||
@@ -212,29 +247,32 @@ describe('Queue API - Queue Control', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.post.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.stopQueue();
|
||||
const result = await QueueAPI.stopQueue();
|
||||
|
||||
expect(global.AniWorld.ApiClient.post).toHaveBeenCalledWith('/api/queue/stop', {});
|
||||
expect(result.message).toBe('Queue stopped');
|
||||
});
|
||||
|
||||
it('should handle start queue error', async () => {
|
||||
global.AniWorld.ApiClient.post.mockRejectedValue(new Error('Already running'));
|
||||
|
||||
await expect(global.AniWorld.QueueAPI.startQueue()).rejects.toThrow('Already running');
|
||||
global.AniWorld.ApiClient.post.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
await expect(QueueAPI.startQueue()).rejects.toThrow('Network error');
|
||||
});
|
||||
|
||||
|
||||
it('should handle stop queue error', async () => {
|
||||
global.AniWorld.ApiClient.post.mockRejectedValue(new Error('Not running'));
|
||||
|
||||
await expect(global.AniWorld.QueueAPI.stopQueue()).rejects.toThrow('Not running');
|
||||
global.AniWorld.ApiClient.post.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
await expect(QueueAPI.stopQueue()).rejects.toThrow('Network error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Queue API - Item Management', () => {
|
||||
let QueueAPI;
|
||||
|
||||
beforeEach(() => {
|
||||
setupDOM();
|
||||
setupMockAniWorld();
|
||||
QueueAPI = loadQueueAPI();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -247,7 +285,7 @@ describe('Queue API - Item Management', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.delete.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.removeFromQueue('item-123');
|
||||
const result = await QueueAPI.removeFromQueue('item-123');
|
||||
|
||||
expect(global.AniWorld.ApiClient.delete).toHaveBeenCalledWith('/api/queue/remove/item-123');
|
||||
expect(result).toBe(true);
|
||||
@@ -260,7 +298,7 @@ describe('Queue API - Item Management', () => {
|
||||
global.AniWorld.ApiClient.post.mockResolvedValue(mockResponse);
|
||||
|
||||
const itemIds = ['item-1', 'item-2'];
|
||||
const result = await global.AniWorld.QueueAPI.retryDownloads(itemIds);
|
||||
const result = await QueueAPI.retryDownloads(itemIds);
|
||||
|
||||
expect(global.AniWorld.ApiClient.post).toHaveBeenCalledWith('/api/queue/retry', { item_ids: itemIds });
|
||||
expect(result.retried).toBe(2);
|
||||
@@ -272,7 +310,7 @@ describe('Queue API - Item Management', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.delete.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.clearCompleted();
|
||||
const result = await QueueAPI.clearCompleted();
|
||||
|
||||
expect(global.AniWorld.ApiClient.delete).toHaveBeenCalledWith('/api/queue/completed');
|
||||
expect(result.cleared).toBe(5);
|
||||
@@ -284,7 +322,7 @@ describe('Queue API - Item Management', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.delete.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.clearFailed();
|
||||
const result = await QueueAPI.clearFailed();
|
||||
|
||||
expect(global.AniWorld.ApiClient.delete).toHaveBeenCalledWith('/api/queue/failed');
|
||||
expect(result.cleared).toBe(3);
|
||||
@@ -296,7 +334,7 @@ describe('Queue API - Item Management', () => {
|
||||
};
|
||||
global.AniWorld.ApiClient.delete.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await global.AniWorld.QueueAPI.clearPending();
|
||||
const result = await QueueAPI.clearPending();
|
||||
|
||||
expect(global.AniWorld.ApiClient.delete).toHaveBeenCalledWith('/api/queue/pending');
|
||||
expect(result.cleared).toBe(2);
|
||||
@@ -339,6 +377,15 @@ describe('Queue Renderer - Statistics Display', () => {
|
||||
});
|
||||
|
||||
it('should handle zero statistics', () => {
|
||||
// Rebuild DOM from scratch
|
||||
document.body.innerHTML = `
|
||||
<span id="pending-count"></span>
|
||||
<span id="active-count"></span>
|
||||
<span id="completed-count"></span>
|
||||
<span id="failed-count"></span>
|
||||
<span id="total-count"></span>
|
||||
`;
|
||||
|
||||
const data = {
|
||||
statistics: {
|
||||
pending: 0,
|
||||
@@ -348,20 +395,21 @@ describe('Queue Renderer - Statistics Display', () => {
|
||||
total: 0
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('pending-count').textContent = data.statistics.pending;
|
||||
document.getElementById('active-count').textContent = data.statistics.active;
|
||||
document.getElementById('completed-count').textContent = data.statistics.completed;
|
||||
document.getElementById('failed-count').textContent = data.statistics.failed;
|
||||
document.getElementById('total-count').textContent = data.statistics.total;
|
||||
|
||||
|
||||
// Use innerHTML to set values directly (avoids textContent coercion issues in JSDOM)
|
||||
document.getElementById('pending-count').innerHTML = data.statistics.pending;
|
||||
document.getElementById('active-count').innerHTML = data.statistics.active;
|
||||
document.getElementById('completed-count').innerHTML = data.statistics.completed;
|
||||
document.getElementById('failed-count').innerHTML = data.statistics.failed;
|
||||
document.getElementById('total-count').innerHTML = data.statistics.total;
|
||||
|
||||
expect(document.getElementById('pending-count').textContent).toBe('0');
|
||||
expect(document.getElementById('active-count').textContent).toBe('0');
|
||||
expect(document.getElementById('completed-count').textContent).toBe('0');
|
||||
expect(document.getElementById('failed-count').textContent).toBe('0');
|
||||
expect(document.getElementById('total-count').textContent).toBe('0');
|
||||
});
|
||||
|
||||
|
||||
it('should update statistics when queue changes', () => {
|
||||
// Initial state
|
||||
document.getElementById('pending-count').textContent = '5';
|
||||
@@ -540,6 +588,7 @@ describe('Queue Button Handlers', () => {
|
||||
beforeEach(() => {
|
||||
setupDOM();
|
||||
setupMockAniWorld();
|
||||
patchQueueAPIDelegate();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -810,6 +859,12 @@ describe('Queue Edge Cases', () => {
|
||||
});
|
||||
|
||||
it('should handle empty queue gracefully', () => {
|
||||
// Rebuild DOM from scratch to guarantee clean state
|
||||
document.body.innerHTML = `
|
||||
<span id="pending-count"></span>
|
||||
<div id="pending-queue"></div>
|
||||
`;
|
||||
|
||||
const data = {
|
||||
statistics: {
|
||||
pending: 0,
|
||||
@@ -823,10 +878,11 @@ describe('Queue Edge Cases', () => {
|
||||
completed_items: [],
|
||||
failed_items: []
|
||||
};
|
||||
|
||||
document.getElementById('pending-count').textContent = data.statistics.pending;
|
||||
|
||||
// Use innerHTML to set values (avoids textContent coercion issues in JSDOM)
|
||||
document.getElementById('pending-count').innerHTML = data.statistics.pending;
|
||||
document.getElementById('pending-queue').innerHTML = '';
|
||||
|
||||
|
||||
expect(document.getElementById('pending-count').textContent).toBe('0');
|
||||
expect(document.getElementById('pending-queue').children.length).toBe(0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user