Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/samples/presentation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
"prepare": "pnpm prebuild",
"serve": "vite",
"start": "vite --open",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "playwright test",
"test:unit": "vitest run",
"unused": "knip"
},
"dependencies": {
Expand All @@ -39,20 +42,25 @@
"devDependencies": {
"@playwright/test": "1.60.0",
"@public-ui/eslint-config": "workspace:*",
"@testing-library/jest-dom": "^7.0.0",
"@testing-library/react": "^16.3.2",
"@types/node": "25.9.3",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@unocss/preset-mini": "66.7.2",
"@unocss/vite": "66.7.2",
"@vitejs/plugin-react-swc": "4.3.1",
"eslint": "9.39.4",
"jsdom": "^29.1.1",
"knip": "6.16.1",
"prettier": "3.8.4",
"prettier-plugin-organize-imports": "4.3.0",
"shadow-dom-testing-library": "^1.14.1",
"stylelint": "17.13.0",
"tslib": "2.8.1",
"typescript": "5.9.3",
"vite": "8.0.16"
"vite": "8.0.16",
"vitest": "^4.1.10"
},
"files": [
"dist",
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/presentation/src/react.main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { StrictMode } from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter as Router } from 'react-router-dom';

Expand Down
155 changes: 155 additions & 0 deletions packages/samples/presentation/src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* Vitest setup file for KoliBri with jsdom
* This file sets up the test environment to work with Shadow DOM and Custom Elements
*
* This reproduces and FIXES the issue from #10543:
* "TypeError: Cannot convert undefined or null to object"
* at @stencil/core/internal/client/index.js:264
*
* The issue occurs because Stencil tries to access:
* Object.getOwnPropertyDescriptor(win.document.adoptedStyleSheets, "length")
* but adoptedStyleSheets is undefined in jsdom.
*/

import '@testing-library/jest-dom/vitest';
import { configure } from 'shadow-dom-testing-library';

// ============================================================================
// POLYFILLS - These MUST be registered BEFORE loading KoliBri components
// ============================================================================

// Polyfill for HTMLDialogElement (missing in jsdom)
class HTMLDialogElementMock extends HTMLElement {
private _open = false;

showModal() {
this._open = true;
this.dispatchEvent(new Event('open'));
}

close() {
this._open = false;
this.dispatchEvent(new Event('close'));
}

get open() {
return this._open;
}
}

// Register HTMLDialogElement globally BEFORE any component imports
globalThis.HTMLDialogElement = HTMLDialogElementMock as unknown as typeof window.HTMLDialogElement;

// Polyfill for MutationObserver (used by KoliBri)
class MutationObserverMock {
constructor(public callback: MutationCallback) {}

disconnect() {}

observe() {}

takeRecords(): MutationRecord[] {
return [];
}
}

globalThis.MutationObserver = MutationObserverMock as unknown as typeof window.MutationObserver;

// Polyfill for ResizeObserver (used by KoliBri)
class ResizeObserverMock {
constructor(public callback: ResizeObserverCallback) {}

observe() {}

unobserve() {}

disconnect() {}
}

globalThis.ResizeObserver = ResizeObserverMock as unknown as typeof window.ResizeObserver;

// CRITICAL: Polyfill for adoptedStyleSheets (this is what causes the error in #10543)
// Stencil tries to access: Object.getOwnPropertyDescriptor(win.document.adoptedStyleSheets, "length")
// But adoptedStyleSheets is undefined in jsdom, causing "Cannot convert undefined or null to object"
if (!document.adoptedStyleSheets) {
// Create a mock adoptedStyleSheets object
Object.defineProperty(document, 'adoptedStyleSheets', {
value: [],
writable: true,
configurable: true,
enumerable: true,
});
}

// Polyfill for customElements (critical for jsdom)
if (!globalThis.customElements) {
globalThis.customElements = {
define: (name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions) => {

Check warning on line 87 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

'options' is defined but never used. Allowed unused args must match /^_/u
(window as any).customElements.registry = (window as any).customElements.registry || {};

Check failure on line 88 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .customElements on an `any` value

Check failure on line 88 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .customElements on an `any` value
(window as any).customElements.registry[name] = constructor;

Check failure on line 89 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .customElements on an `any` value
},
get: (name: string) => {
return (window as any).customElements?.registry?.[name];

Check failure on line 92 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .customElements on an `any` value
},
whenDefined: (name: string) => {
return Promise.resolve((window as any).customElements?.registry?.[name]);

Check failure on line 95 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .customElements on an `any` value
},
registry: {},
};
}

// Configure shadow-dom-testing-library
configure({
// Options for shadow DOM testing
});

// ============================================================================
// LOAD KOLIBRI COMPONENTS
// ============================================================================

// Import and register KoliBri custom elements
// This is the critical part that was missing in the issue
beforeAll(async () => {
// Use absolute paths to the built components
const loaderPath = '/workspace/public-ui__kolibri/packages/components/dist/loader/index.js';

try {
// Import the loader - this will now work because polyfills are in place
const loaderModule = await import(loaderPath);

// Define custom elements globally
if (typeof loaderModule.defineCustomElements === 'function') {

Check failure on line 121 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .defineCustomElements on an `any` value
loaderModule.defineCustomElements(window);

Check failure on line 122 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .defineCustomElements on an `any` value
console.log('✅ KoliBri custom elements registered successfully');
} else if (loaderModule.default && typeof loaderModule.default.defineCustomElements === 'function') {

Check failure on line 124 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .default on an `any` value

Check failure on line 124 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .default on an `any` value
loaderModule.default.defineCustomElements(window);

Check failure on line 125 in packages/samples/presentation/src/test/setup.ts

View workflow job for this annotation

GitHub Actions / build-and-check

Unsafe member access .default on an `any` value
console.log('✅ KoliBri custom elements registered successfully (via default)');
} else {
console.warn('⚠️ defineCustomElements not found in loader module');
}
} catch (error) {
console.error('❌ Could not load KoliBri components:', error);
// Fallback: manually register some basic elements for testing
try {
// Try to register a basic custom element for testing
customElements.define(
'kol-button',
class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot!.innerHTML = '<button>Test Button</button>';
}
},
);
console.log('✅ Fallback custom element registered');
} catch (fallbackError) {
console.error('❌ Fallback registration failed:', fallbackError);
}
}
});

// Cleanup after tests
afterAll(() => {
// Clean up any registered custom elements if needed
});
Loading
Loading