UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

385 lines (342 loc) 10.8 kB
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Error Handling Tests</title> </head> <body> <script type="module"> import { runTests } from '@web/test-runner-mocha' import { assert } from '@esm-bundle/chai' import { component, state, asString, asInteger, } from '../index.dev.js' runTests(() => { describe('Component Definition Errors', () => { it('should throw InvalidComponentNameError for invalid names', () => { assert.throws( () => component('invalid', {}, () => []), Error, 'Invalid component name "invalid". Custom element names must contain a hyphen', ) assert.throws( () => component('Invalid-Name', {}, () => []), Error, 'Invalid component name "Invalid-Name". Custom element names must contain a hyphen', ) assert.throws( () => component('123-invalid', {}, () => []), Error, 'Invalid component name "123-invalid". Custom element names must contain a hyphen', ) }) it('should throw InvalidPropertyNameError for reserved words', () => { assert.throws( () => { component( 'test-reserved', { constructor: asString('invalid'), }, () => [], ) }, Error, 'Invalid property name "constructor" for component <test-reserved>. Property name "constructor" is a reserved word', ) }) it('should throw InvalidPropertyNameError for HTMLElement conflicts', () => { assert.throws( () => { component( 'test-html-conflict', { innerHTML: asString('invalid'), }, () => [], ) }, Error, 'Invalid property name "innerHTML" for component <test-html-conflict>. Property name "innerHTML" conflicts with inherited HTMLElement property', ) assert.throws( () => { component( 'test-classname-conflict', { className: asString('invalid'), }, () => [], ) }, Error, 'Invalid property name "className" for component <test-classname-conflict>. Property name "className" conflicts with inherited HTMLElement property', ) }) it('should throw for multiple invalid properties', () => { assert.throws( () => { component( 'test-multiple-invalid', { validProp: asString('valid'), id: asString('invalid'), // HTMLElement conflict anotherValid: asInteger(0), }, () => [], ) }, Error, 'Invalid property name "id" for component <test-multiple-invalid>. Property name "id" conflicts with inherited HTMLElement property', ) }) }) describe('Runtime Property Errors', () => { before(() => { // Define a test component for runtime tests component( 'test-runtime-errors', { validProp: asString('valid'), }, () => [], ) }) it('should throw InvalidSignalError for non-signal values', () => { const instance = document.createElement( 'test-runtime-errors', ) assert.throws( () => { instance.setSignal( 'invalidProp', 'not a signal', ) }, Error, 'Expected signal as value', ) assert.throws( () => { instance.setSignal('anotherInvalid', { not: 'a signal', }) }, Error, 'Expected signal as value', ) }) it('should throw InvalidPropertyNameError for reserved words at runtime', () => { const instance = document.createElement( 'test-runtime-errors', ) assert.throws( () => { instance.setSignal( 'constructor', state('invalid'), ) }, Error, 'Property name "constructor" is a reserved word', ) }) it('should throw InvalidPropertyNameError for HTMLElement conflicts at runtime', () => { const instance = document.createElement( 'test-runtime-errors', ) assert.throws( () => { instance.setSignal( 'innerHTML', state('invalid'), ) }, Error, 'Property name "innerHTML" conflicts with inherited HTMLElement property', ) }) }) describe('Missing Element Errors', () => { it('should verify error types are defined', () => { // Test that the error constructors exist and work assert.throws( () => { throw new Error( 'Missing required element <.test> in component <test>. reason', ) }, Error, 'Missing required element', ) }) it('should verify components can be created despite potential setup errors', () => { // Test that components with potential missing elements can be defined component( 'test-missing-simple', {}, (_, { useElement }) => { // Don't actually call useElement with required param to avoid error // This tests the component definition itself return [] }, ) const instance = document.createElement( 'test-missing-simple', ) assert.instanceOf(instance, HTMLElement) }) it('should verify error handling infrastructure works', () => { // Test error message format const errorMessage = 'Missing required element <.test> in component <test-comp>. needed for testing' assert.include(errorMessage, 'Missing required element') assert.include(errorMessage, 'needed for testing') }) }) describe('Parser Errors', () => { it('should handle parser functions that throw errors', () => { component( 'test-parser-error', { value: (el, value) => { if (value === 'error') { throw new Error('Parser error') } return value || 'default' }, }, () => [], ) const instance = document.createElement('test-parser-error') document.body.appendChild(instance) // Should not crash when setting valid attribute instance.setAttribute('value', 'valid') assert.equal(instance.value, 'valid') document.body.removeChild(instance) }) it('should handle null and undefined attribute values gracefully', () => { // Test parser behavior with different input values component( 'test-null-attributes-simple', { testValue: (el, value) => { if (value === null) return 'null-value' if (value === undefined) return 'undefined-value' if (value === '') return 'empty-string' return value || 'fallback' }, }, () => [], ) const instance = document.createElement( 'test-null-attributes-simple', ) // Test that component is created successfully assert.instanceOf(instance, HTMLElement) assert.equal( instance.localName, 'test-null-attributes-simple', ) }) }) describe('Setup Function Errors', () => { it('should verify components can be created with various return types', () => { // Test that component creation works with simple valid effects component('test-valid-effects', {}, () => { return [] // Valid return type }) const instance = document.createElement('test-valid-effects') assert.instanceOf(instance, HTMLElement) assert.equal(instance.localName, 'test-valid-effects') }) it('should verify error message formatting for invalid effects', () => { // Test that error messages are properly formatted const errorMessage = 'Invalid effects in component <<test>>. Effects must be an array of effects, a single effect function, or a Promise that resolves to effects.' assert.include(errorMessage, 'Invalid effects') assert.include(errorMessage, 'Effects must be') }) }) describe('Circular Dependency Errors', () => { it('should throw CircularMutationError for infinite loops', () => { component( 'test-circular-mutation', {}, (_, { useElement }) => { // This could potentially cause circular mutations // if the selector observes elements that are modified by the component const target = useElement('div') if (target) { // Simulate a scenario that could cause circular mutations // The actual circular detection happens in fromSelector target.innerHTML = '<span>Modified</span>' } return [] }, ) const instance = document.createElement( 'test-circular-mutation', ) instance.innerHTML = '<div></div>' // This should not cause infinite loops assert.doesNotThrow(() => { document.body.appendChild(instance) document.body.removeChild(instance) }) }) }) describe('Dependency Timeout Errors', () => { it('should handle DependencyTimeoutError gracefully', async () => { // Define a component that depends on a non-existent custom element component('test-timeout-error', {}, (_, { first }) => [ first('non-existent-element', []), ]) const instance = document.createElement('test-timeout-error') instance.innerHTML = '<non-existent-element></non-existent-element>' // Component should be created assert.instanceOf(instance, HTMLElement) // Adding to DOM should not crash despite dependency timeout assert.doesNotThrow(() => { document.body.appendChild(instance) }) // Clean up document.body.removeChild(instance) }) }) describe('Invalid Initializer Errors', () => { it('should handle undefined and null initializers gracefully', () => { component( 'test-invalid-initializers', { validProp: asString('valid'), undefinedProp: undefined, nullProp: null, }, () => [], ) const instance = document.createElement( 'test-invalid-initializers', ) document.body.appendChild(instance) // Valid prop should work assert.equal(instance.validProp, 'valid') // Invalid props should not crash assert.isUndefined(instance.undefinedProp) assert.isUndefined(instance.nullProp) document.body.removeChild(instance) }) }) }) </script> </body> </html>