UNPKG

als-component

Version:

lightweight JavaScript library for creating reactive, server-rendered, and browser-based components.

259 lines (210 loc) 9.46 kB
describe("Component Class Tests", () => { // Тест на создание экземпляра it("should create an instance of Component", () => { class TestComponent extends Component { render() { return `<div>Hello World</div>`; } } const instance = new TestComponent(); assert(instance instanceof Component, "Instance is not of type Component"); }); // Тест на асинхронность метода render it("should correctly detect if render method is async", () => { class SyncComponent extends Component { render() { return `<div>Sync Render</div>`; } } class AsyncComponent extends Component { async render() { return `<div>Async Render</div>`; } } assert.strictEqual(SyncComponent.isAsync, false, "SyncComponent should not be async"); assert.strictEqual(AsyncComponent.isAsync, true, "AsyncComponent should be async"); }); // Тест на метод call() it("should call the render method and return HTML", () => { class SimpleComponent extends Component { constructor() {super()} render() { return `<div>Simple Render</div>`; } } const instance = new SimpleComponent(); const html = instance.call(); assert.strictEqual(html, `<div component="SimpleComponent">Simple Render</div>`, "call() did not return expected HTML"); }); // Тест на метод action() it("should add an action and return an ID", () => { class ButtonComponent extends Component { render() { return `<button click="${this.action('click', () => this.update())}">Click Me</button>`; } } const instance = new ButtonComponent(); const actionId = instance.action('click', () => { }); assert(actionId.startsWith("ButtonComponent"), "Action ID does not start with component name"); }); // Тест на обновление компонента it("should update the component with new props", () => { class CounterComponent extends Component { render({ count = 0 }) { return `<div>${count}</div>`; } } document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="CounterComponent"></div>`) const instance = new CounterComponent({ count: 1 }); instance.update({ count: 2 }); const element = document.querySelector(instance.selector); assert.strictEqual(element.textContent, "2", "Component did not update with new props"); element.remove() }); // Тест на выполнение хуков mount и unmount it("should trigger mount and unmount hooks", () => { let mountTriggered = false; let unmountTriggered = false; document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="HookComponent"></div>`) class HookComponent extends Component { render() { return `<div>Hook Component</div>`; } } const instance = new HookComponent(); instance.on("mount", () => (mountTriggered = true)); instance.on("unmount", () => (unmountTriggered = true)); instance.update(); assert(mountTriggered, "Mount hook was not triggered"); // Удаляем элемент из DOM для проверки unmount document.querySelector(instance.selector).remove(); instance.runActions(); assert(unmountTriggered, "Unmount hook was not triggered"); }); // Тест на работу события load it("should handle load event and focus input", async () => { document.body.insertAdjacentHTML('beforeend',/*html*/`<input type="text" component="InputComponent" tabindex="0">`) class InputComponent extends Component { constructor() { super() } render() { return `<input type="text" load="${this.action('load', (element) => element.focus())}">`; } } const instance = new InputComponent(); await instance.update(); const input = document.querySelector("input"); assert(document.activeElement === input, "Input element did not receive focus on load"); input.remove() }); // Тест на удаление старых обработчиков событий it("should remove old event listeners after update", () => { let clickCount = 0; document.body.insertAdjacentHTML('beforeend',/*html*/`<button component="ClickComponent"></button>`) class ClickComponent extends Component { constructor(props) { super(props) } render({ count = 0 }) { return `<button click="${this.action('click', () => { clickCount++ })}">Click Me ${count}</button>`; } } const instance = new ClickComponent({ count: 1 }); instance.update({ count: 2 }); const button = document.querySelector("button"); button.click(); button.click(); // console.log(clickCount) assert.strictEqual(clickCount, 2, "Old event listeners were not removed"); button.remove() }); }); describe('Additional tests', () => { it("should use the inner content correctly", () => { class InnerComponent extends Component { render(props, inner) { return `<div>${inner}</div>`; } } const instance = new InnerComponent({}, "Inner Content"); const html = instance.call(); assert.strictEqual(html, `<div component="InnerComponent">Inner Content</div>`, "Inner content was not rendered correctly"); }); it("should update the component multiple times with the same props", () => { let updateCount = 0; document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="RepeatUpdateComponent"></div>`) class RepeatUpdateComponent extends Component { render({ count = 0 }) { updateCount++; return `<div>${count}</div>`; } } const instance = new RepeatUpdateComponent({ count: 1 }); instance.update(); instance.update(); assert.strictEqual(updateCount, 2, "Component was not updated twice"); instance.element.remove() }); it("should handle async render method in update", async () => { document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="AsyncRenderComponent"></div>`) class AsyncRenderComponent extends Component { constructor(props) {super(props)} async render() { return new Promise((resolve) => setTimeout(() => resolve(`<div>Async Render</div>`), 100)); } } const instance = new AsyncRenderComponent(); await instance.update(); const element = document.querySelector(instance.selector); assert.strictEqual(element.textContent, "Async Render", "Async render method did not work correctly"); element.remove(); }); it("should not throw error if element is not found", () => { class MissingElementComponent extends Component { render() { return `<div>Missing Element</div>`; } } const instance = new MissingElementComponent(); try { instance.update() assert(true); } catch (error) { assert(false, "Update method threw an error when element was not found"); } }); it("should handle multiple uses of the same action without duplicating handlers", () => { let clickCount = 0; document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="MultiActionComponent"></div>`) class MultiActionComponent extends Component { render() { const clickHandler = this.action('click', () => clickCount++); return `<div> <button click="${clickHandler}">Click 1</button> <button click="${clickHandler}">Click 2</button> </div>`; } } const instance = new MultiActionComponent(); instance.update(); const buttons = document.querySelectorAll("button"); buttons[0].click(); buttons[1].click(); assert.strictEqual(clickCount, 2, "Click handler was not called correctly for multiple uses"); instance.element.remove() }); it("should remove component from WeakMap after unmount", () => { document.body.insertAdjacentHTML('beforeend',/*html*/`<div component="CleanupComponent"></div>`) class CleanupComponent extends Component { render() { return `<div>Cleanup Component</div>`; } } const instance = new CleanupComponent(); instance.update(); const element = document.querySelector(instance.selector); element.remove(); instance.runActions(); // const isInWeakMap = Component.components.has(instance.name); // assert.strictEqual(isInWeakMap, false, "Component was not removed from WeakMap after unmount"); const isInWeakMap = Component.components.has(instance); assert.strictEqual(isInWeakMap, false, "Component was not removed from WeakMap after unmount"); }); })