Skip to content

Commit efd9598

Browse files
committed
added component tests
1 parent ace10e7 commit efd9598

File tree

3 files changed

+520
-0
lines changed

3 files changed

+520
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { Injector } from '@furystack/inject'
2+
import { createComponent, initializeShadeRoot } from '@furystack/shades'
3+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
4+
5+
import { Widget } from './widget.js'
6+
7+
describe('Widget', () => {
8+
beforeEach(() => {
9+
document.body.innerHTML = '<div id="root"></div>'
10+
// Mock animate for jsdom (used by skeleton, loader, and other animations)
11+
HTMLElement.prototype.animate = vi.fn().mockReturnValue({
12+
finished: Promise.resolve(),
13+
cancel: vi.fn(),
14+
})
15+
vi.useFakeTimers()
16+
})
17+
18+
afterEach(() => {
19+
document.body.innerHTML = ''
20+
vi.useRealTimers()
21+
})
22+
23+
it('should render AppShortcutWidget for app-shortcut type', () => {
24+
const injector = new Injector()
25+
const rootElement = document.getElementById('root') as HTMLDivElement
26+
27+
initializeShadeRoot({
28+
injector,
29+
rootElement,
30+
jsxElement: <Widget type="app-shortcut" appName="home" />,
31+
})
32+
33+
const widget = document.querySelector('pi-rat-widget')
34+
expect(widget).toBeTruthy()
35+
36+
const appShortcut = widget?.querySelector('pi-rat-app-shortcut-widget')
37+
expect(appShortcut).toBeTruthy()
38+
})
39+
40+
it('should render EntityShortcutWidget for entity-shortcut type', () => {
41+
const injector = new Injector()
42+
const rootElement = document.getElementById('root') as HTMLDivElement
43+
44+
initializeShadeRoot({
45+
injector,
46+
rootElement,
47+
jsxElement: <Widget type="entity-shortcut" entityName="movie" />,
48+
})
49+
50+
const widget = document.querySelector('pi-rat-widget')
51+
expect(widget).toBeTruthy()
52+
53+
const entityShortcut = widget?.querySelector('pi-rat-entity-shortcut-widget')
54+
expect(entityShortcut).toBeTruthy()
55+
})
56+
57+
it('should render HtmlWidget for html type', () => {
58+
const injector = new Injector()
59+
const rootElement = document.getElementById('root') as HTMLDivElement
60+
61+
initializeShadeRoot({
62+
injector,
63+
rootElement,
64+
jsxElement: <Widget type="html" content="<p>Hello World</p>" />,
65+
})
66+
67+
const widget = document.querySelector('pi-rat-widget')
68+
expect(widget).toBeTruthy()
69+
70+
const htmlWidget = widget?.querySelector('pi-rat-html-widget')
71+
expect(htmlWidget).toBeTruthy()
72+
expect(htmlWidget?.innerHTML).toContain('Hello World')
73+
})
74+
75+
it('should render MarkdownWidget for markdown type', () => {
76+
const injector = new Injector()
77+
const rootElement = document.getElementById('root') as HTMLDivElement
78+
79+
initializeShadeRoot({
80+
injector,
81+
rootElement,
82+
jsxElement: <Widget type="markdown" content="# Heading" />,
83+
})
84+
85+
const widget = document.querySelector('pi-rat-widget')
86+
expect(widget).toBeTruthy()
87+
88+
const markdownWidget = widget?.querySelector('pi-rat-markdown-widget')
89+
expect(markdownWidget).toBeTruthy()
90+
})
91+
92+
it('should render WidgetGroup for group type', () => {
93+
const injector = new Injector()
94+
const rootElement = document.getElementById('root') as HTMLDivElement
95+
96+
initializeShadeRoot({
97+
injector,
98+
rootElement,
99+
jsxElement: <Widget type="group" title="Test Group" widgets={[]} />,
100+
})
101+
102+
const widget = document.querySelector('pi-rat-widget')
103+
expect(widget).toBeTruthy()
104+
105+
const widgetGroup = widget?.querySelector('pi-rat-widget-group')
106+
expect(widgetGroup).toBeTruthy()
107+
})
108+
109+
it('should render MovieWidget for movie type', () => {
110+
const injector = new Injector()
111+
const rootElement = document.getElementById('root') as HTMLDivElement
112+
113+
initializeShadeRoot({
114+
injector,
115+
rootElement,
116+
jsxElement: <Widget type="movie" imdbId="tt1234567" />,
117+
})
118+
119+
const widget = document.querySelector('pi-rat-widget')
120+
expect(widget).toBeTruthy()
121+
122+
const movieWidget = widget?.querySelector('pi-rat-movie-widget')
123+
expect(movieWidget).toBeTruthy()
124+
})
125+
126+
it('should render SeriesWidget for series type', () => {
127+
const injector = new Injector()
128+
const rootElement = document.getElementById('root') as HTMLDivElement
129+
130+
initializeShadeRoot({
131+
injector,
132+
rootElement,
133+
jsxElement: <Widget type="series" imdbId="tt7654321" />,
134+
})
135+
136+
const widget = document.querySelector('pi-rat-widget')
137+
expect(widget).toBeTruthy()
138+
139+
const seriesWidget = widget?.querySelector('pi-rat-series-widget')
140+
expect(seriesWidget).toBeTruthy()
141+
})
142+
143+
it('should render ContinueWatchingWidgetGroup for continue-watching type', () => {
144+
const injector = new Injector()
145+
const rootElement = document.getElementById('root') as HTMLDivElement
146+
147+
initializeShadeRoot({
148+
injector,
149+
rootElement,
150+
jsxElement: <Widget type="continue-watching" />,
151+
})
152+
153+
const widget = document.querySelector('pi-rat-widget')
154+
expect(widget).toBeTruthy()
155+
156+
const continueWatching = widget?.querySelector('continue-watching-widget-group')
157+
expect(continueWatching).toBeTruthy()
158+
})
159+
160+
// Note: DeviceAvailability widget test is skipped because it requires complex
161+
// service mocking (IotDevicesService, SessionService) that's beyond the scope
162+
// of a simple component routing test. The routing logic is verified by the
163+
// other widget type tests above.
164+
})
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Injector } from '@furystack/inject'
2+
import { createComponent, initializeShadeRoot } from '@furystack/shades'
3+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
4+
5+
import { SettingsMenuItem } from './settings-menu-item.js'
6+
7+
describe('SettingsMenuItem', () => {
8+
beforeEach(() => {
9+
document.body.innerHTML = '<div id="root"></div>'
10+
})
11+
12+
afterEach(() => {
13+
document.body.innerHTML = ''
14+
})
15+
16+
it('should render with icon, label, and href', () => {
17+
const injector = new Injector()
18+
const rootElement = document.getElementById('root') as HTMLDivElement
19+
20+
initializeShadeRoot({
21+
injector,
22+
rootElement,
23+
jsxElement: <SettingsMenuItem icon={<span>🏠</span>} label="Home" href="/home" />,
24+
})
25+
26+
const menuItem = document.querySelector('settings-menu-item')
27+
expect(menuItem).toBeTruthy()
28+
29+
const link = menuItem?.querySelector('a')
30+
expect(link).toBeTruthy()
31+
expect(link?.getAttribute('href')).toBe('/home')
32+
expect(link?.textContent).toContain('Home')
33+
expect(link?.textContent).toContain('🏠')
34+
})
35+
36+
it('should render with active state styling', () => {
37+
const injector = new Injector()
38+
const rootElement = document.getElementById('root') as HTMLDivElement
39+
40+
initializeShadeRoot({
41+
injector,
42+
rootElement,
43+
jsxElement: <SettingsMenuItem icon={<span>⚙️</span>} label="Settings" href="/settings" isActive={true} />,
44+
})
45+
46+
const menuItem = document.querySelector('settings-menu-item')
47+
expect(menuItem).toBeTruthy()
48+
49+
const link = menuItem?.querySelector('a')
50+
expect(link).toBeTruthy()
51+
expect(link?.getAttribute('href')).toBe('/settings')
52+
expect(link?.textContent).toContain('Settings')
53+
})
54+
55+
it('should render with inactive state by default', () => {
56+
const injector = new Injector()
57+
const rootElement = document.getElementById('root') as HTMLDivElement
58+
59+
initializeShadeRoot({
60+
injector,
61+
rootElement,
62+
jsxElement: <SettingsMenuItem icon={<span>📁</span>} label="Files" href="/files" />,
63+
})
64+
65+
const menuItem = document.querySelector('settings-menu-item')
66+
expect(menuItem).toBeTruthy()
67+
68+
const link = menuItem?.querySelector('a')
69+
expect(link).toBeTruthy()
70+
expect(link?.getAttribute('href')).toBe('/files')
71+
})
72+
73+
it('should render with explicit inactive state', () => {
74+
const injector = new Injector()
75+
const rootElement = document.getElementById('root') as HTMLDivElement
76+
77+
initializeShadeRoot({
78+
injector,
79+
rootElement,
80+
jsxElement: <SettingsMenuItem icon={<span>🎬</span>} label="Movies" href="/movies" isActive={false} />,
81+
})
82+
83+
const menuItem = document.querySelector('settings-menu-item')
84+
expect(menuItem).toBeTruthy()
85+
86+
const link = menuItem?.querySelector('a')
87+
expect(link).toBeTruthy()
88+
expect(link?.getAttribute('href')).toBe('/movies')
89+
expect(link?.textContent).toContain('Movies')
90+
})
91+
})

0 commit comments

Comments
 (0)