mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-03-22 20:43:10 -04:00
refactor(frontend): replace legacy utils JS files with typed TS equivalents
This commit is contained in:
@@ -1,44 +1,96 @@
|
||||
// Mock the dispatcher module used by exportStore
|
||||
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ register: jest.fn() }));
|
||||
import EventEmitter from 'events';
|
||||
import { exportStore } from '../../../src/static/js/utils/helpers';
|
||||
|
||||
import exportStore from '../../../src/static/js/utils/helpers/exportStore';
|
||||
// The dispatcher is an external module dependency used by exportStore; mock it to observe registrations
|
||||
jest.mock('../../../src/static/js/utils/dispatcher', () => ({
|
||||
dispatcher: {
|
||||
register: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Re-import the mocked dispatcher for assertions
|
||||
import * as dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||
import { dispatcher } from '../../../src/static/js/utils/dispatcher';
|
||||
|
||||
describe('js/utils/helpers', () => {
|
||||
/**
|
||||
* Behaviors covered:
|
||||
* 1. Binds the provided handler method to store instance context.
|
||||
* 2. Registers the bound callback exactly once with the dispatcher.
|
||||
* 3. Returns the same store instance that was provided.
|
||||
* 4. Invoking the registered callback forwards payload to the handler with correct this.
|
||||
* 5. Type-safety assumption: only function keys are accepted as handler (runtime sanity via mock class).
|
||||
*/
|
||||
|
||||
describe('utils/helpers', () => {
|
||||
describe('exportStore', () => {
|
||||
class TestStore extends (EventEmitter as { new (): EventEmitter }) {
|
||||
public calls: unknown[] = [];
|
||||
public handler(payload: unknown) {
|
||||
// Assert `this` is the store instance when called via bound function
|
||||
this.calls.push({ self: this, payload });
|
||||
}
|
||||
public otherHandler(payload: unknown) {
|
||||
this.calls.push({ self: this, payload, type: 'other' });
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('Registers store handler with dispatcher and binds context', () => {
|
||||
const ctx: { value: number; inc?: () => void } = { value: 0 };
|
||||
const handlerName = 'inc';
|
||||
const handler = function (this: typeof ctx) {
|
||||
this.value += 1;
|
||||
};
|
||||
ctx[handlerName] = handler as any;
|
||||
|
||||
const result = exportStore(ctx, handlerName);
|
||||
|
||||
// returns the same store instance
|
||||
expect(result).toBe(ctx);
|
||||
|
||||
// Ensure dispatcher.register was called once with a bound function
|
||||
expect((dispatcher as any).register).toHaveBeenCalledTimes(1);
|
||||
const registeredFn = (dispatcher as any).register.mock.calls[0][0] as Function;
|
||||
expect(typeof registeredFn).toBe('function');
|
||||
|
||||
// Verify the registered function is bound to the store context
|
||||
registeredFn();
|
||||
expect(ctx.value).toBe(1);
|
||||
test('Returns the same store instance and registers exactly once', () => {
|
||||
const store = new TestStore();
|
||||
const returned = exportStore(store as unknown as EventEmitter, 'handler' as any);
|
||||
expect(returned).toBe(store);
|
||||
expect(dispatcher.register).toHaveBeenCalledTimes(1);
|
||||
expect(typeof (dispatcher.register as jest.Mock).mock.calls[0][0]).toBe('function');
|
||||
});
|
||||
|
||||
test('Throws if handler name does not exist on store', () => {
|
||||
const store: any = {};
|
||||
// Accessing store[handler] would be undefined; calling .bind on undefined would throw
|
||||
expect(() => exportStore(store, 'missing')).toThrow();
|
||||
test('Binds the handler to the store instance context', () => {
|
||||
const store = new TestStore();
|
||||
exportStore(store as unknown as EventEmitter, 'handler' as any);
|
||||
|
||||
const registered = (dispatcher.register as jest.Mock).mock.calls[0][0] as (p: unknown) => void;
|
||||
const payload = { a: 1 };
|
||||
registered(payload);
|
||||
|
||||
expect(store.calls).toHaveLength(1);
|
||||
const { self, payload: received } = store.calls[0] as any;
|
||||
expect(self).toBe(store);
|
||||
expect(received).toBe(payload);
|
||||
});
|
||||
|
||||
test('Forwards any payload through the registered callback to the handler', () => {
|
||||
const store = new TestStore();
|
||||
exportStore(store as unknown as EventEmitter, 'otherHandler' as any);
|
||||
|
||||
const registered = (dispatcher.register as jest.Mock).mock.calls[0][0] as (p: unknown) => void;
|
||||
registered(null);
|
||||
registered(42);
|
||||
registered({ x: 'y' });
|
||||
|
||||
expect(store.calls.map((c: any) => c.payload)).toEqual([null, 42, { x: 'y' }]);
|
||||
});
|
||||
|
||||
test('Supports different handler keys of the same store', () => {
|
||||
const store = new TestStore();
|
||||
exportStore(store as unknown as EventEmitter, 'handler' as any);
|
||||
exportStore(store as unknown as EventEmitter, 'otherHandler' as any);
|
||||
|
||||
expect(dispatcher.register).toHaveBeenCalledTimes(2);
|
||||
const cb1 = (dispatcher.register as jest.Mock).mock.calls[0][0] as (p: unknown) => void;
|
||||
const cb2 = (dispatcher.register as jest.Mock).mock.calls[1][0] as (p: unknown) => void;
|
||||
|
||||
cb1('a');
|
||||
cb2('b');
|
||||
|
||||
expect(store.calls).toHaveLength(2);
|
||||
expect(store.calls[0]).toMatchObject({ payload: 'a' });
|
||||
expect(store.calls[1]).toMatchObject({ payload: 'b', type: 'other' });
|
||||
});
|
||||
|
||||
test('Runtime guard scenario: non-existing handler results in TypeError on bind access', () => {
|
||||
const store = new TestStore();
|
||||
// @ts-expect-error intentionally passing wrong key to simulate runtime misuse
|
||||
expect(() => exportStore(store as unknown as EventEmitter, 'notAHandler')).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user