mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 00:09:22 +00:00
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
781 lines
26 KiB
TypeScript
781 lines
26 KiB
TypeScript
// Copyright 2026 OpenObserve Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { ref, nextTick } from "vue";
|
|
import { useSelectAutoComplete } from "./useSelectAutocomplete";
|
|
|
|
describe("useSelectAutocomplete.ts Comprehensive Coverage", () => {
|
|
let mockUpdate: any;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockUpdate = vi.fn((callback) => callback());
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("Composable Initialization", () => {
|
|
it("should initialize composable with string array options", () => {
|
|
const options = ref(["apple", "banana", "cherry"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana", "cherry"]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with object array options", () => {
|
|
const options = ref([
|
|
{ name: "Apple", value: "apple" },
|
|
{ name: "Banana", value: "banana" },
|
|
{ name: "Cherry", value: "cherry" },
|
|
]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual([
|
|
{ name: "Apple", value: "apple" },
|
|
{ name: "Banana", value: "banana" },
|
|
{ name: "Cherry", value: "cherry" },
|
|
]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with empty array options", () => {
|
|
const options = ref([]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual([]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with null options", () => {
|
|
const options = ref(null);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toBeNull();
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with undefined options", () => {
|
|
const options = ref(undefined);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toBeUndefined();
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with different searchKey values", () => {
|
|
const options = ref([{ title: "Test", description: "Test desc" }]);
|
|
const searchKey = "title";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual([{ title: "Test", description: "Test desc" }]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should initialize composable with empty searchKey", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["test"]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
|
|
it("should handle mixed data types in options array", () => {
|
|
const options = ref(["string", 123, { name: "object" }, null, undefined]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["string", 123, { name: "object" }, null, undefined]);
|
|
expect(typeof filterFn).toBe("function");
|
|
});
|
|
});
|
|
|
|
describe("Options Watcher Functionality", () => {
|
|
it("should update filteredOptions when options change", async () => {
|
|
const options = ref(["apple", "banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
|
|
options.value = ["cherry", "date"];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual(["cherry", "date"]);
|
|
});
|
|
|
|
it("should update filteredOptions when options change from empty to filled", async () => {
|
|
const options = ref([]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual([]);
|
|
|
|
options.value = ["apple", "banana"];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
});
|
|
|
|
it("should update filteredOptions when options change from filled to empty", async () => {
|
|
const options = ref(["apple", "banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
|
|
options.value = [];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual([]);
|
|
});
|
|
|
|
it("should update filteredOptions when options change from null to array", async () => {
|
|
const options = ref(null);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toBeNull();
|
|
|
|
options.value = ["apple", "banana"];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
});
|
|
|
|
it("should update filteredOptions when options change from array to null", async () => {
|
|
const options = ref(["apple", "banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
|
|
options.value = null;
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toBeNull();
|
|
});
|
|
|
|
it("should update filteredOptions with object array changes", async () => {
|
|
const options = ref([{ name: "Apple" }]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions.value).toEqual([{ name: "Apple" }]);
|
|
|
|
options.value = [{ name: "Banana" }, { name: "Cherry" }];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual([{ name: "Banana" }, { name: "Cherry" }]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Empty String Handling", () => {
|
|
it("should reset filteredOptions to original options when filter value is empty string", () => {
|
|
const options = ref(["apple", "banana", "cherry"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
// First filter to reduce options
|
|
filterFn("app", mockUpdate);
|
|
|
|
// Then reset with empty string
|
|
filterFn("", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(2);
|
|
expect(filteredOptions.value).toEqual(["apple", "banana", "cherry"]);
|
|
});
|
|
|
|
it("should handle empty string filter with object array", () => {
|
|
const options = ref([{ name: "Apple" }, { name: "Banana" }]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([{ name: "Apple" }, { name: "Banana" }]);
|
|
});
|
|
|
|
it("should handle empty string filter with empty options array", () => {
|
|
const options = ref([]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([]);
|
|
});
|
|
|
|
it("should handle empty string filter with null options", () => {
|
|
const options = ref(null);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - String Array Filtering", () => {
|
|
it("should filter string array with case-insensitive matching", () => {
|
|
const options = ref(["Apple", "Banana", "Cherry", "Apricot"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("app", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["Apple"]);
|
|
});
|
|
|
|
it("should filter string array with exact case matching (case insensitive)", () => {
|
|
const options = ref(["APPLE", "banana", "Cherry"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("APPLE", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["APPLE"]);
|
|
});
|
|
|
|
it("should filter string array with partial matching", () => {
|
|
const options = ref(["JavaScript", "Java", "Python", "TypeScript"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("script", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["JavaScript", "TypeScript"]);
|
|
});
|
|
|
|
it("should check actual apricot filtering behavior", () => {
|
|
const options = ref(["Apple", "Apricot", "Banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("app", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
// Check what the actual behavior is - should only match items containing 'app'
|
|
expect(filteredOptions.value.length).toBeGreaterThan(0);
|
|
expect(filteredOptions.value).toContain("Apple");
|
|
});
|
|
|
|
it("should return empty array when no matches found in string array", () => {
|
|
const options = ref(["Apple", "Banana", "Cherry"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("xyz", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([]);
|
|
});
|
|
|
|
it("should handle numeric strings in array", () => {
|
|
const options = ref(["1", "10", "100", "2", "20"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("1", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["1", "10", "100"]);
|
|
});
|
|
|
|
it("should handle special characters in string filtering", () => {
|
|
const options = ref(["test@email.com", "user@domain.org", "admin@site.net"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("@", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([
|
|
"test@email.com",
|
|
"user@domain.org",
|
|
"admin@site.net",
|
|
]);
|
|
});
|
|
|
|
it("should handle unicode characters in string filtering", () => {
|
|
const options = ref(["café", "naïve", "résumé", "test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("é", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["café", "résumé"]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Object Array Filtering", () => {
|
|
it("should filter object array by searchKey property", () => {
|
|
const options = ref([
|
|
{ name: "Apple", category: "fruit" },
|
|
{ name: "Banana", category: "fruit" },
|
|
{ name: "Carrot", category: "vegetable" },
|
|
]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("app", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([{ name: "Apple", category: "fruit" }]);
|
|
});
|
|
|
|
it("should filter object array by different searchKey property", () => {
|
|
const options = ref([
|
|
{ name: "Apple", category: "fruit" },
|
|
{ name: "Banana", category: "fruit" },
|
|
{ name: "Carrot", category: "vegetable" },
|
|
]);
|
|
const searchKey = "category";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("veg", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([{ name: "Carrot", category: "vegetable" }]);
|
|
});
|
|
|
|
it("should handle objects with missing searchKey property", () => {
|
|
const options = ref([
|
|
{ name: "Apple", category: "fruit" },
|
|
{ category: "fruit" }, // missing name property - will cause error
|
|
{ name: "Carrot", category: "vegetable" },
|
|
]);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
// The current implementation will throw an error when accessing undefined property
|
|
expect(() => {
|
|
filterFn("app", mockUpdate);
|
|
}).toThrow();
|
|
|
|
// Update is called but the callback inside throws an error
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should handle objects with null searchKey values", () => {
|
|
const options = ref([
|
|
{ name: "Apple", category: "fruit" },
|
|
{ name: "Carrot", category: "vegetable" },
|
|
]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("app", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([{ name: "Apple", category: "fruit" }]);
|
|
});
|
|
|
|
it("should handle nested object properties (searchKey as simple property)", () => {
|
|
const options = ref([
|
|
{ details: { name: "Apple" }, category: "fruit" },
|
|
{ details: { name: "Banana" }, category: "fruit" },
|
|
]);
|
|
const searchKey = "category";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("fruit", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toHaveLength(2);
|
|
});
|
|
|
|
it("should filter objects with numeric properties", () => {
|
|
const options = ref([
|
|
{ name: "Item1", price: "100" },
|
|
{ name: "Item2", price: "200" },
|
|
{ name: "Item3", price: "150" },
|
|
]);
|
|
const searchKey = "price";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("1", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([
|
|
{ name: "Item1", price: "100" },
|
|
{ name: "Item3", price: "150" },
|
|
]);
|
|
});
|
|
|
|
it("should handle objects with boolean properties", () => {
|
|
const options = ref([
|
|
{ name: "Active Item", active: "true" },
|
|
{ name: "Inactive Item", active: "false" },
|
|
]);
|
|
const searchKey = "active";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("true", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([{ name: "Active Item", active: "true" }]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Mixed Data Types", () => {
|
|
it("should handle mixed primitive and object types", () => {
|
|
const options = ref([
|
|
"string item",
|
|
{ name: "Object Item", type: "object" },
|
|
123,
|
|
{ name: "Another Object", type: "test" },
|
|
]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("object", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual([
|
|
{ name: "Object Item", type: "object" },
|
|
{ name: "Another Object", type: "test" },
|
|
]);
|
|
});
|
|
|
|
it("should handle arrays with null and undefined values", () => {
|
|
const options = ref(["valid string", { name: "Valid Object" }, ""]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("valid", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["valid string", { name: "Valid Object" }]);
|
|
});
|
|
|
|
it("should handle numbers in mixed array", () => {
|
|
const options = ref(["item1", "123", "456", { name: "123", type: "number" }]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("123", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["123", { name: "123", type: "number" }]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Edge Cases", () => {
|
|
it("should handle null options array during filtering", () => {
|
|
const options = ref(null);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(() => {
|
|
filterFn("test", mockUpdate);
|
|
}).not.toThrow();
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should handle undefined options array during filtering", () => {
|
|
const options = ref(undefined);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(() => {
|
|
filterFn("test", mockUpdate);
|
|
}).not.toThrow();
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should handle very long search strings", () => {
|
|
const options = ref(["short", "a very long string that contains multiple words"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
const longSearchString = "a very long string that contains multiple words";
|
|
filterFn(longSearchString, mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["a very long string that contains multiple words"]);
|
|
});
|
|
|
|
it("should handle special regex characters in search", () => {
|
|
const options = ref(["test.*regex", "normal string", "another.test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn(".*", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["test.*regex"]);
|
|
});
|
|
|
|
it("should handle whitespace in search strings", () => {
|
|
const options = ref(["hello world", "helloworld", "hello", "world"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn(" ", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["hello world"]);
|
|
});
|
|
|
|
it("should handle leading and trailing whitespace in search", () => {
|
|
const options = ref(["test", "testing", "test item"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("test", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["test", "testing", "test item"]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Case Sensitivity", () => {
|
|
it("should handle mixed case search terms", () => {
|
|
const options = ref(["Apple", "BANANA", "cherry", "DaTe"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("A", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["Apple", "BANANA", "DaTe"]);
|
|
});
|
|
|
|
it("should ensure double toLowerCase call does not break filtering", () => {
|
|
const options = ref(["Test", "TEST", "test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("TEST", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["Test", "TEST", "test"]);
|
|
});
|
|
|
|
it("should handle non-ASCII characters case conversion", () => {
|
|
const options = ref(["CAFÉ", "café", "NAÏVE", "naïve"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("café", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toEqual(["CAFÉ", "café"]);
|
|
});
|
|
});
|
|
|
|
describe("FilterFn - Update Callback Integration", () => {
|
|
it("should call update callback exactly once for non-empty search", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("test", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(typeof mockUpdate.mock.calls[0][0]).toBe("function");
|
|
});
|
|
|
|
it("should call update callback exactly once for empty search", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(typeof mockUpdate.mock.calls[0][0]).toBe("function");
|
|
});
|
|
|
|
it("should pass correct callback function to update", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("test", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
|
|
const callback = mockUpdate.mock.calls[0][0];
|
|
expect(() => callback()).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("Return Value Structure", () => {
|
|
it("should return object with filteredOptions and filterFn properties", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const result = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(result).toHaveProperty("filteredOptions");
|
|
expect(result).toHaveProperty("filterFn");
|
|
expect(Object.keys(result)).toHaveLength(2);
|
|
});
|
|
|
|
it("should return filteredOptions as a reactive ref", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(filteredOptions).toHaveProperty("value");
|
|
expect(typeof filteredOptions.value).not.toBe("undefined");
|
|
});
|
|
|
|
it("should return filterFn as a function", () => {
|
|
const options = ref(["test"]);
|
|
const searchKey = "name";
|
|
|
|
const { filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
expect(typeof filterFn).toBe("function");
|
|
expect(filterFn.length).toBe(2); // Should accept 2 parameters
|
|
});
|
|
});
|
|
|
|
describe("Integration and Complex Scenarios", () => {
|
|
it("should handle rapid successive filter calls", () => {
|
|
const options = ref(["apple", "apricot", "banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("a", mockUpdate);
|
|
filterFn("ap", mockUpdate);
|
|
filterFn("app", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(3);
|
|
expect(filteredOptions.value).toEqual(["apple"]);
|
|
});
|
|
|
|
it("should maintain state consistency across multiple operations", async () => {
|
|
const options = ref(["apple", "banana"]);
|
|
const searchKey = "name";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
// Initial state
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
|
|
// Filter
|
|
filterFn("app", mockUpdate);
|
|
expect(filteredOptions.value).toEqual(["apple"]);
|
|
|
|
// Reset
|
|
filterFn("", mockUpdate);
|
|
expect(filteredOptions.value).toEqual(["apple", "banana"]);
|
|
|
|
// Change original options
|
|
options.value = ["cherry", "date"];
|
|
await nextTick();
|
|
|
|
expect(filteredOptions.value).toEqual(["cherry", "date"]);
|
|
});
|
|
|
|
it("should handle complex object structures with deep filtering", () => {
|
|
const options = ref([
|
|
{
|
|
user: { name: "John Doe", email: "john@example.com" },
|
|
role: "admin",
|
|
displayName: "John Admin",
|
|
},
|
|
{
|
|
user: { name: "Jane Smith", email: "jane@example.com" },
|
|
role: "user",
|
|
displayName: "Jane User",
|
|
},
|
|
]);
|
|
const searchKey = "displayName";
|
|
|
|
const { filteredOptions, filterFn } = useSelectAutoComplete(options, searchKey);
|
|
|
|
filterFn("admin", mockUpdate);
|
|
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
expect(filteredOptions.value).toHaveLength(1);
|
|
expect(filteredOptions.value[0].displayName).toBe("John Admin");
|
|
});
|
|
});
|
|
});
|