/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/lib/src/hooks/useExecuteWhenChanged.test.ts
188 строк
6 KB
Ken McGrady
Update copyright header to 2026 (#13491)
02 янв 2026, 16:21
Не верифицирован
02 янв 2026, 16:21
374540a
Код
Авторство
О чём код?
/** * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { renderHook } from "@testing-library/react" import { vi } from "vitest" import { arrayComparator, useExecuteWhenChanged, } from "./useExecuteWhenChanged" describe("#useExecuteWhenChanged", () => { it("should not call the callback when the value is the same", () => { const callback = vi.fn() const { rerender } = renderHook( ({ value }) => useExecuteWhenChanged(() => callback(value), [value]), { initialProps: { value: 1 } } ) expect(callback).not.toHaveBeenCalled() rerender({ value: 1 }) rerender({ value: 1 }) expect(callback).not.toHaveBeenCalled() }) it("should execute the callback when the value changes", () => { const callback = vi.fn() const { rerender } = renderHook( ({ value }) => useExecuteWhenChanged(() => callback(value), [value]), { initialProps: { value: 1 } } ) // Initial render should not call the callback expect(callback).not.toHaveBeenCalled() // Rerender with the same value should not call the callback again callback.mockClear() rerender({ value: 1 }) expect(callback).not.toHaveBeenCalled() // Rerender with a different value should call the callback callback.mockClear() rerender({ value: 2 }) expect(callback).toHaveBeenCalledWith(2) expect(callback).toHaveBeenCalledTimes(1) }) it("should handle stabilized objects as expected", () => { const callback = vi.fn() const value = { id: 1, name: "test" } const { rerender } = renderHook( ({ value }) => useExecuteWhenChanged(() => callback(value), [value]), { initialProps: { value } } ) expect(callback).not.toHaveBeenCalled() rerender({ value }) expect(callback).not.toHaveBeenCalled() }) it("should use custom comparator when provided", () => { const callback = vi.fn() const customComparator = vi.fn(([prev], [curr]) => prev === curr) const { rerender } = renderHook( ({ value }) => useExecuteWhenChanged( () => callback(value), [value], customComparator ), { initialProps: { value: "test value" } } ) // Initial render should not call the callback expect(callback).not.toHaveBeenCalled() // Expect the custom comparator to have been called expect(customComparator).toHaveBeenCalledTimes(1) // Rerender with new value rerender({ value: "updated" }) // The custom comparator should have been called 2 more times because the // value has changed. On the first re-render, the comparator is called with // the initial value and the new value. The callback is called on this run, // which sets state. On the second re-render, the comparator is called again // with the previous value and the new value. The callback is not called // again because the value has not changed. expect(customComparator).toHaveBeenCalledTimes(3) // The callback should have been called only once because the value has changed expect(callback).toHaveBeenCalledTimes(1) }) describe("#arrayComparator", () => { it.each([ { a: [1, 2, 3], b: [1, 2, 3], expected: true, description: "identical primitive arrays", }, { a: [1, 2, 3], b: [1, 2, 4], expected: false, description: "different primitive arrays", }, { a: [], b: [], expected: true, description: "empty arrays", }, { a: [1], b: [1, 2], expected: false, description: "different length arrays", }, { a: [null, undefined], b: [null, undefined], expected: true, description: "arrays with null and undefined", }, { a: [null], b: [undefined], expected: false, description: "null vs undefined", }, { a: [NaN], b: [NaN], expected: true, description: "arrays with NaN", }, { a: [NaN, 1], b: [NaN, 1], expected: true, description: "arrays with NaN and numbers", }, ])("should return $expected for $description", ({ a, b, expected }) => { expect(arrayComparator(a, b)).toBe(expected) }) it("should handle arrays with object references correctly", () => { const obj1 = { id: 1 } const obj2 = { id: 2 } // Same object references should be equal expect(arrayComparator([obj1, obj2], [obj1, obj2])).toBe(true) // Different object references with same content should be different expect(arrayComparator([obj1, obj2], [obj1, { id: 2 }])).toBe(false) expect(arrayComparator([{ id: 1 }], [{ id: 1 }])).toBe(false) }) it("should handle nested arrays", () => { const inner = [2, 3] // Same nested array references should be equal expect(arrayComparator([1, inner], [1, inner])).toBe(true) // Different nested array references should be different expect(arrayComparator([1, [2, 3]], [1, [2, 3]])).toBe(false) expect(arrayComparator([1, [2, 3]], [1, [2, 4]])).toBe(false) }) }) })