mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2026-06-14 22:18:33 +00:00
489 lines
14 KiB
TypeScript
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");
|
|
});
|
|
});
|
|
});
|