Files
skywalking-booster-ui/src/components/__tests__/SelectSingle.spec.ts

489 lines
14 KiB
TypeScript

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { mount } from "@vue/test-utils";
import { nextTick } from "vue";
import SelectSingle from "../SelectSingle.vue";
describe("SelectSingle Component", () => {
let wrapper: Recordable;
const mockOptions = [
{ label: "Option 1", value: "option1" },
{ label: "Option 2", value: "option2" },
{ label: "Option 3", value: "option3" },
];
beforeEach(() => {
vi.clearAllMocks();
// Mock document.body.addEventListener
vi.spyOn(document.body, "addEventListener").mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("Props", () => {
it("should render with default props", () => {
wrapper = mount(SelectSingle);
expect(wrapper.exists()).toBe(true);
expect(wrapper.find(".bar-select").exists()).toBe(true);
expect(wrapper.find(".no-data").text()).toBe("Please select a option");
});
it("should render with custom options", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
expect(wrapper.props("options")).toEqual(mockOptions);
});
it("should render with selected value", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
expect(wrapper.vm.selected.value).toBe("option1");
expect(wrapper.vm.selected.label).toBe("Option 1");
});
it("should render with clearable option", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
clearable: true,
},
});
expect(wrapper.props("clearable")).toBe(true);
expect(wrapper.find(".remove-icon").exists()).toBe(true);
});
it("should not show remove icon when clearable is false", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
clearable: false,
},
});
expect(wrapper.find(".remove-icon").exists()).toBe(false);
});
});
describe("Component Structure", () => {
it("should have correct template structure", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
expect(wrapper.find(".bar-select").exists()).toBe(true);
expect(wrapper.find(".bar-i").exists()).toBe(true);
expect(wrapper.find(".opt-wrapper").exists()).toBe(true);
});
it("should render options correctly", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
const options = wrapper.findAll(".opt");
expect(options.length).toBe(3);
expect(options[0].text()).toBe("Option 1");
expect(options[1].text()).toBe("Option 2");
expect(options[2].text()).toBe("Option 3");
});
it("should show selected option text", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
expect(wrapper.find(".bar-i span").text()).toBe("Option 1");
});
it("should show placeholder when no option is selected", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "",
},
});
expect(wrapper.find(".no-data").text()).toBe("Please select a option");
});
});
describe("Event Handling", () => {
it("should emit change event when option is selected", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
// Open dropdown
await wrapper.find(".bar-i").trigger("click");
await nextTick();
// Select an option
await wrapper.find(".opt").trigger("click");
expect(wrapper.emitted("change")).toBeTruthy();
expect(wrapper.emitted("change")[0][0]).toBe("option1");
});
it("should emit change event with empty string when remove is clicked", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
clearable: true,
},
});
await wrapper.find(".remove-icon").trigger("click");
expect(wrapper.emitted("change")).toBeTruthy();
expect(wrapper.emitted("change")[0][0]).toBe("");
});
it("should toggle dropdown visibility when bar-i is clicked", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
expect(wrapper.vm.visible).toBe(false);
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.vm.visible).toBe(true);
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.vm.visible).toBe(false);
});
it("should not select disabled option", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
// Open dropdown
await wrapper.find(".bar-i").trigger("click");
await nextTick();
// Try to select the already selected option (which should be disabled)
const disabledOption = wrapper.find(".select-disabled");
expect(disabledOption.exists()).toBe(true);
expect(disabledOption.text()).toBe("Option 1");
});
});
describe("Watchers", () => {
it("should update selected value when props.value changes", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
expect(wrapper.vm.selected.value).toBe("option1");
await wrapper.setProps({
value: "option2",
});
await nextTick();
expect(wrapper.vm.selected.value).toBe("option2");
expect(wrapper.vm.selected.label).toBe("Option 2");
});
it("should handle value change to empty string", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
expect(wrapper.vm.selected.value).toBe("option1");
await wrapper.setProps({
value: "",
});
await nextTick();
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("");
});
it("should handle value change to non-existent option", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
expect(wrapper.vm.selected.value).toBe("option1");
await wrapper.setProps({
value: "nonexistent",
});
await nextTick();
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("");
});
});
describe("Methods", () => {
it("should handle select option correctly", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
await wrapper.vm.handleSelect(mockOptions[1]);
expect(wrapper.vm.selected.value).toBe("option2");
expect(wrapper.vm.selected.label).toBe("Option 2");
expect(wrapper.emitted("change")[0][0]).toBe("option2");
});
it("should handle remove selected correctly", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
await wrapper.vm.removeSelected();
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("");
expect(wrapper.emitted("change")[0][0]).toBe("");
});
it("should handle setPopper correctly", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
const event = { stopPropagation: vi.fn() };
await wrapper.vm.setPopper(event);
expect(event.stopPropagation).toHaveBeenCalled();
expect(wrapper.vm.visible).toBe(true);
});
it("should handle click outside correctly", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
// Open dropdown
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.vm.visible).toBe(true);
// Simulate click outside
await wrapper.vm.handleClick();
expect(wrapper.vm.visible).toBe(false);
});
});
describe("Edge Cases", () => {
it("should handle empty options array", () => {
wrapper = mount(SelectSingle, {
props: {
options: [],
},
});
expect(wrapper.props("options")).toEqual([]);
expect(wrapper.findAll(".opt").length).toBe(0);
});
it("should handle undefined value", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: undefined,
},
});
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("");
});
it("should handle null value", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: null as any,
},
});
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("");
});
it("should handle options with empty values", () => {
const optionsWithEmptyValues = [
{ label: "Option 1", value: "" },
{ label: "Option 2", value: "option2" },
];
wrapper = mount(SelectSingle, {
props: {
options: optionsWithEmptyValues,
value: "",
},
});
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.vm.selected.label).toBe("Option 1");
});
});
describe("Integration", () => {
it("should work with all props combined", () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
clearable: true,
},
});
expect(wrapper.exists()).toBe(true);
expect(wrapper.vm.selected.value).toBe("option1");
expect(wrapper.vm.selected.label).toBe("Option 1");
expect(wrapper.props("clearable")).toBe(true);
expect(wrapper.find(".remove-icon").exists()).toBe(true);
});
it("should handle complete selection workflow", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
clearable: true,
},
});
// Initially no selection
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.find(".no-data").text()).toBe("Please select a option");
// Open dropdown
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.vm.visible).toBe(true);
// Select an option
await wrapper.find(".opt").trigger("click");
expect(wrapper.vm.selected.value).toBe("option1");
expect(wrapper.emitted("change")[0][0]).toBe("option1");
// Clear selection
await wrapper.find(".remove-icon").trigger("click");
expect(wrapper.vm.selected.value).toBe("");
expect(wrapper.emitted("change")[1][0]).toBe("");
});
it("should handle dropdown toggle and option selection", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
// Toggle dropdown
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.vm.visible).toBe(true);
// Select option
const options = wrapper.findAll(".opt");
await options[1].trigger("click");
expect(wrapper.vm.selected.value).toBe("option2");
expect(wrapper.emitted("change")[0][0]).toBe("option2");
expect(wrapper.vm.visible).toBe(true); // Should stay open after selection
});
});
describe("CSS Classes", () => {
it("should apply active class when dropdown is visible", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
},
});
expect(wrapper.find(".bar-select").classes()).not.toContain("active");
await wrapper.find(".bar-i").trigger("click");
expect(wrapper.find(".bar-select").classes()).toContain("active");
});
it("should apply select-disabled class to selected option", async () => {
wrapper = mount(SelectSingle, {
props: {
options: mockOptions,
value: "option1",
},
});
await wrapper.find(".bar-i").trigger("click");
await nextTick();
const options = wrapper.findAll(".opt");
expect(options[0].classes()).toContain("select-disabled");
expect(options[1].classes()).not.toContain("select-disabled");
expect(options[2].classes()).not.toContain("select-disabled");
});
});
});