mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 04:42:57 +00:00
refactor(ai): extract notification logic from js/app.js (agent)
- Moved `showError`, `showStatus`, `showSuccess`, and `updateNotificationPortalVisibility` logic to `js/ui/notificationController.js`. - Instantiated `NotificationController` in `js/ui/applicationBootstrap.js`. - Refactored `js/app.js` to delegate notification calls to the controller. - Added unit tests for `NotificationController`. - Followed AGENTS.md refactoring checklist. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
cd6e5862d7
commit
1dfc23c11a
4 files changed
+424
-201
No files matched your search
@@ -0,0 +1,133 @@
|
||||
import { describe, it, beforeEach, afterEach, mock } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import NotificationController from '../../js/ui/notificationController.js';
|
||||
|
||||
// Simple mock for HTMLElement
|
||||
class MockHTMLElement {
|
||||
constructor() {
|
||||
this.classList = {
|
||||
contains: mock.fn(() => false),
|
||||
add: mock.fn(),
|
||||
remove: mock.fn(),
|
||||
toggle: mock.fn(),
|
||||
};
|
||||
this.textContent = '';
|
||||
this.querySelector = mock.fn(() => null);
|
||||
this.contains = mock.fn(() => false);
|
||||
this.insertBefore = mock.fn();
|
||||
this.remove = mock.fn();
|
||||
this.firstChild = null;
|
||||
this.setAttribute = mock.fn();
|
||||
}
|
||||
}
|
||||
|
||||
describe('NotificationController', () => {
|
||||
let controller;
|
||||
let mockPortal;
|
||||
let mockErrorContainer;
|
||||
let mockSuccessContainer;
|
||||
let mockStatusContainer;
|
||||
let mockWindow;
|
||||
let mockDocument;
|
||||
let mockUserLogger;
|
||||
let mockDevLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPortal = new MockHTMLElement();
|
||||
mockErrorContainer = new MockHTMLElement();
|
||||
mockSuccessContainer = new MockHTMLElement();
|
||||
mockStatusContainer = new MockHTMLElement();
|
||||
|
||||
mockWindow = {
|
||||
HTMLElement: MockHTMLElement,
|
||||
setTimeout: mock.fn((cb, ms) => setTimeout(cb, ms)),
|
||||
clearTimeout: mock.fn((id) => clearTimeout(id)),
|
||||
};
|
||||
|
||||
mockDocument = {
|
||||
createElement: mock.fn(() => new MockHTMLElement()),
|
||||
};
|
||||
|
||||
mockUserLogger = { error: mock.fn() };
|
||||
mockDevLogger = { log: mock.fn() };
|
||||
|
||||
controller = new NotificationController({
|
||||
portal: mockPortal,
|
||||
errorContainer: mockErrorContainer,
|
||||
successContainer: mockSuccessContainer,
|
||||
statusContainer: mockStatusContainer,
|
||||
loggers: { userLogger: mockUserLogger, devLogger: mockDevLogger },
|
||||
documentRef: mockDocument,
|
||||
windowRef: mockWindow,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
controller.destroy();
|
||||
mock.reset();
|
||||
});
|
||||
|
||||
it('should instantiate correctly', () => {
|
||||
assert.strictEqual(controller.portal, mockPortal);
|
||||
assert.strictEqual(controller.errorContainer, mockErrorContainer);
|
||||
});
|
||||
|
||||
it('showError should update text content and show container', () => {
|
||||
const msg = 'Test Error';
|
||||
controller.showError(msg);
|
||||
|
||||
assert.strictEqual(mockErrorContainer.textContent, msg);
|
||||
assert.strictEqual(mockErrorContainer.classList.remove.mock.calls.length, 1);
|
||||
assert.strictEqual(mockErrorContainer.classList.remove.mock.calls[0].arguments[0], 'hidden');
|
||||
assert.strictEqual(mockUserLogger.error.mock.calls.length, 1);
|
||||
assert.strictEqual(mockUserLogger.error.mock.calls[0].arguments[0], msg);
|
||||
});
|
||||
|
||||
it('showError should hide container when msg is empty', () => {
|
||||
controller.showError('');
|
||||
|
||||
assert.strictEqual(mockErrorContainer.textContent, '');
|
||||
assert.strictEqual(mockErrorContainer.classList.add.mock.calls.length, 1);
|
||||
assert.strictEqual(mockErrorContainer.classList.add.mock.calls[0].arguments[0], 'hidden');
|
||||
});
|
||||
|
||||
it('updateNotificationPortalVisibility should toggle active class on portal', () => {
|
||||
// Simulate error container being visible
|
||||
mockErrorContainer.classList.contains = mock.fn((cls) => cls !== 'hidden');
|
||||
|
||||
controller.updateNotificationPortalVisibility();
|
||||
|
||||
assert.strictEqual(mockPortal.classList.toggle.mock.calls.length, 1);
|
||||
assert.strictEqual(mockPortal.classList.toggle.mock.calls[0].arguments[0], 'notification-portal--active');
|
||||
assert.strictEqual(mockPortal.classList.toggle.mock.calls[0].arguments[1], true);
|
||||
});
|
||||
|
||||
it('showSuccess should update text content and show container', () => {
|
||||
const msg = 'Success!';
|
||||
controller.showSuccess(msg);
|
||||
|
||||
assert.strictEqual(mockSuccessContainer.textContent, msg);
|
||||
assert.strictEqual(mockSuccessContainer.classList.remove.mock.calls.length, 1);
|
||||
assert.strictEqual(mockSuccessContainer.classList.remove.mock.calls[0].arguments[0], 'hidden');
|
||||
});
|
||||
|
||||
it('showStatus should update text content and show container', () => {
|
||||
const msg = 'Status update';
|
||||
controller.showStatus(msg);
|
||||
|
||||
assert.strictEqual(mockStatusContainer.textContent, msg);
|
||||
assert.strictEqual(mockStatusContainer.classList.remove.mock.calls.length, 1);
|
||||
assert.strictEqual(mockStatusContainer.classList.remove.mock.calls[0].arguments[0], 'hidden');
|
||||
});
|
||||
|
||||
it('showStatus should handle spinner option', () => {
|
||||
const msg = 'Loading...';
|
||||
const mockSpinner = new MockHTMLElement();
|
||||
mockDocument.createElement = mock.fn(() => mockSpinner);
|
||||
|
||||
controller.showStatus(msg, { showSpinner: true });
|
||||
|
||||
assert.strictEqual(mockDocument.createElement.mock.calls.length, 1);
|
||||
assert.strictEqual(mockStatusContainer.insertBefore.mock.calls.length, 1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user