inula/packages/inula-next/test/components.test.tsx

248 lines
6.1 KiB
TypeScript

/*
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { describe, expect, vi } from 'vitest';
import { domTest as it } from './utils';
import { render } from '../src';
vi.mock('../src/scheduler', async () => {
return {
schedule: (task: () => void) => {
task();
},
};
});
describe('components', () => {
describe('ref', () => {
it('should support ref', ({ container }) => {
let ref: HTMLElement;
function App() {
let count = 0;
let _ref: HTMLElement;
didMount(() => {
ref = _ref;
});
return <div ref={_ref}>test</div>;
}
render(App, container);
expect(ref).toBeInstanceOf(HTMLElement);
});
it('should support ref forwarding', ({ container }) => {
let ref: HTMLElement;
function App() {
let count = 0;
let _ref: HTMLElement;
didMount(() => {
ref = _ref;
});
return <Input ref={_ref}>test</Input>;
}
function Input({ ref }) {
return <input ref={ref} />;
}
render(App, container);
expect(ref).toBeInstanceOf(HTMLInputElement);
});
it('should support ref with function', ({ container }) => {
const fn = vi.fn();
function App() {
const ref = (el: HTMLElement) => {
fn();
expect(el).toBeInstanceOf(HTMLElement);
};
return <div ref={ref}>test</div>;
}
render(App, container);
expect(fn).toHaveBeenCalledTimes(1);
});
it('should support comp ref exposing', ({ container }) => {
let ref: HTMLElement;
const fn = vi.fn();
function App() {
let count = 0;
let _ref;
didMount(() => {
ref = _ref;
_ref.fn();
});
return <Input ref={_ref}>test</Input>;
}
function Input({ ref }) {
let input;
didMount(() => {
ref({ fn, input });
});
return <input ref={input} />;
}
render(App, container);
expect(fn).toHaveBeenCalledTimes(1);
expect(ref.input).toBeInstanceOf(HTMLInputElement);
});
});
describe('composition', () => {
it('should update prop', ({ container }) => {
let update: (name: string) => void;
function App() {
let name = 'child';
update = (val: string) => {
name = val;
};
return <Child name={name} />;
}
function Child({ name }: { name: string }) {
return <div>name is {name}</div>;
}
render(App, container);
expect(container.innerHTML).toBe('<div>name is child</div>');
update('new');
expect(container.innerHTML).toBe('<div>name is new</div>');
});
});
describe('nested component', () => {
it('should render sub component using parent state', ({ container }) => {
function App() {
let count = 0;
function Heading() {
return <h1>{count}</h1>;
}
return <Heading />;
}
render(App, container);
expect(container.innerHTML).toBe('<h1>0</h1>');
});
it('should update nested component when parent state changes', ({ container }) => {
let setCount: (n: number) => void;
function App() {
let count = 0;
setCount = (n: number) => {
count = n;
};
function Counter() {
return <div>Count: {count}</div>;
}
return <Counter />;
}
render(App, container);
expect(container.innerHTML).toBe('<div>Count: 0</div>');
setCount(5);
expect(container.innerHTML).toBe('<div>Count: 5</div>');
});
it('should pass props through multiple levels of nesting', ({ container }) => {
function App() {
let name = 'Alice';
function Parent({ children }) {
return (
<div className="parent">
<h2>Parent</h2>
{children}
</div>
);
}
function Child({ name }: { name: string }) {
return <div className="child">Hello, {name}!</div>;
}
return (
<Parent>
<Child name={name} />
</Parent>
);
}
render(App, container);
expect(container.innerHTML).toBe(
'<div class="parent"><h2>Parent</h2><div class="child">Hello, Alice!</div></div>'
);
});
it('should handle sibling nested components with independent state', ({ container }) => {
let incrementA: () => void;
let incrementB: () => void;
function App() {
let a = 0;
let b = 0;
incrementA = () => {
a += 1;
};
incrementB = () => {
b += 1;
};
function Counter({ name }: { name: string }) {
const value = name === 'A' ? a : b;
return (
<div>
Counter {name}: {value}
</div>
);
}
return (
<div>
<Counter name="A" />
<Counter name="B" />
</div>
);
}
render(App, container);
expect(container.innerHTML).toBe('<div><div>Counter A: 0</div><div>Counter B: 0</div></div>');
incrementA();
expect(container.innerHTML).toBe('<div><div>Counter A: 1</div><div>Counter B: 0</div></div>');
incrementB();
expect(container.innerHTML).toBe('<div><div>Counter A: 1</div><div>Counter B: 1</div></div>');
});
});
});