Skip to content

Commit 479ebd6

Browse files
authored
Merge pull request #740 from sugarlabs/brickInputtest
tests(masonry): Add the tests for brickInput
2 parents 69b3bca + b557ab9 commit 479ebd6

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Component test for BrickViewInput. Unlike the pure-math path2 specs, this
2+
// renders the React component into a DOM (jsdom) via React Testing Library and
3+
// asserts on the produced markup.
4+
//
5+
// Scope note: jsdom does no real layout (getBoundingClientRect() returns zeros,
6+
// SVG geometry is not computed), so this file only covers what the component
7+
// sets *declaratively* — the widget's color, font size, and value rendering. The outline
8+
// path generation is geometry and is covered by path2 specs, which need no DOM.
9+
10+
import { cleanup, render, screen } from '@testing-library/react';
11+
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
12+
13+
import type { WidgetInput } from '@/@types/brick.types';
14+
15+
import { ValueBrickModel } from '@/models/brick';
16+
import { SCALE_LEVEL_CONFIG } from '@/utils/constants';
17+
18+
import { BrickViewInput } from './BrickInput';
19+
20+
beforeAll(() => {
21+
vi.stubGlobal(
22+
'ResizeObserver',
23+
class {
24+
observe() {}
25+
unobserve() {}
26+
disconnect() {}
27+
},
28+
);
29+
});
30+
31+
afterEach(cleanup);
32+
33+
const colorsDefault = {
34+
background: '#4f46e5',
35+
foreground: '#ffffff',
36+
border: '#4338ca',
37+
};
38+
39+
/** Renders a value brick with an interactive input widget via a model. */
40+
function renderValueBrickInput(widget: WidgetInput, scaleLevel: 1 | 2 | 3 = 2) {
41+
const model = new ValueBrickModel({
42+
colorsDefault,
43+
tooltipText: '',
44+
widget,
45+
});
46+
model.scaleLevel = scaleLevel;
47+
48+
return render(
49+
<BrickViewInput kind="value" model={model as ValueBrickModel & { widget: WidgetInput }} />,
50+
);
51+
}
52+
53+
describe('BrickViewInput widget styling and scaling', () => {
54+
it('uses the configured foreground color for the input widget', async () => {
55+
renderValueBrickInput({ type: 'textbox', value: 'Hello' });
56+
57+
const input = await screen.findByDisplayValue('Hello');
58+
59+
// Compare the rendered values directly against a DOM element normalized by jsdom
60+
// (e.g. "#ffffff" -> "rgb(255, 255, 255)"), so equality is robust.
61+
const dummy = document.createElement('span');
62+
dummy.style.color = colorsDefault.foreground;
63+
64+
expect(input.style.color).toBe(dummy.style.color);
65+
});
66+
67+
it('uses the full label font size (1.0x) from SCALE_LEVEL_CONFIG', async () => {
68+
const scaleLevel = 2;
69+
renderValueBrickInput({ type: 'textbox', value: 'Hello' }, scaleLevel);
70+
71+
const { fontSize } = SCALE_LEVEL_CONFIG[scaleLevel];
72+
const input = await screen.findByDisplayValue('Hello');
73+
74+
expect(input.style.fontSize).toBe(`${fontSize}px`);
75+
});
76+
77+
it('scales the widget font with the brick scale level', async () => {
78+
const scaleLevel = 3;
79+
renderValueBrickInput({ type: 'textbox', value: 'Hello' }, scaleLevel);
80+
81+
const { fontSize } = SCALE_LEVEL_CONFIG[scaleLevel];
82+
const input = await screen.findByDisplayValue('Hello');
83+
84+
expect(input.style.fontSize).toBe(`${fontSize}px`);
85+
});
86+
});
87+
88+
describe('BrickViewInput widget types', () => {
89+
it('renders a textbox widget with default value and maxLength', async () => {
90+
renderValueBrickInput({ type: 'textbox', value: 'Hello world', maxLength: 15 });
91+
92+
const input = (await screen.findByDisplayValue('Hello world')) as HTMLInputElement;
93+
expect(input.tagName).toBe('INPUT');
94+
expect(input.type).toBe('text');
95+
expect(input.value).toBe('Hello world');
96+
expect(input.maxLength).toBe(15);
97+
});
98+
99+
it('renders a numberbox widget with default value, min, max, and step', async () => {
100+
renderValueBrickInput({ type: 'numberbox', value: 42, min: 0, max: 100, step: 5 });
101+
102+
const input = (await screen.findByDisplayValue('42')) as HTMLInputElement;
103+
expect(input.tagName).toBe('INPUT');
104+
expect(input.type).toBe('number');
105+
expect(input.value).toBe('42');
106+
expect(input.min).toBe('0');
107+
expect(input.max).toBe('100');
108+
expect(input.step).toBe('5');
109+
});
110+
111+
it('renders a toggle widget with checked state and labels', async () => {
112+
renderValueBrickInput({
113+
type: 'toggle',
114+
value: true,
115+
labels: { on: 'Active', off: 'Inactive' },
116+
});
117+
118+
const checkbox = (await screen.findByRole('checkbox')) as HTMLInputElement;
119+
expect(checkbox.checked).toBe(true);
120+
expect(screen.getByText('Active')).toBeDefined();
121+
expect(screen.getByText('Inactive')).toBeDefined();
122+
});
123+
124+
it('renders a slider widget with min and max labels', async () => {
125+
renderValueBrickInput({ type: 'slider', value: 50, min: 10, max: 90, step: 10 });
126+
127+
expect(await screen.findByText('10')).toBeDefined();
128+
expect(screen.getByText('90')).toBeDefined();
129+
});
130+
131+
it('renders a select widget with options and default value', async () => {
132+
renderValueBrickInput({
133+
type: 'select',
134+
options: ['Option A', 'Option B', 'Option C'],
135+
value: 'Option B',
136+
});
137+
138+
const trigger = await screen.findByRole('combobox');
139+
expect(trigger).toBeDefined();
140+
expect(screen.getByText('Option B')).toBeDefined();
141+
});
142+
});

0 commit comments

Comments
 (0)