mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
247 lines
7.5 KiB
JavaScript
247 lines
7.5 KiB
JavaScript
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
import { mount } from "@vue/test-utils";
|
|
import { nextTick } from "vue";
|
|
import PLocationInput from "component/location/input.vue";
|
|
|
|
describe("PLocationInput", () => {
|
|
let wrapper;
|
|
|
|
const defaultProps = {
|
|
latlng: [null, null],
|
|
disabled: false,
|
|
hideDetails: true,
|
|
label: "Location",
|
|
placeholder: "37.75267, -122.543",
|
|
density: "comfortable",
|
|
validateOn: "input",
|
|
showMapButton: false,
|
|
icon: "mdi-map-marker",
|
|
mapButtonTitle: "Open Map",
|
|
mapButtonDisabled: false,
|
|
enableUndo: false,
|
|
autoApply: true,
|
|
debounceDelay: 1000,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (wrapper) {
|
|
wrapper.unmount();
|
|
}
|
|
vi.useRealTimers();
|
|
vi.clearAllTimers();
|
|
});
|
|
|
|
const createWrapper = (props = {}) => {
|
|
return mount(PLocationInput, {
|
|
props: { ...defaultProps, ...props },
|
|
});
|
|
};
|
|
|
|
describe("Component Rendering", () => {
|
|
it("should render input field with correct placeholder", () => {
|
|
const placeholder = "Custom placeholder";
|
|
wrapper = createWrapper({ placeholder });
|
|
|
|
const input = wrapper.find("input");
|
|
expect(input.exists()).toBe(true);
|
|
expect(input.attributes("placeholder")).toBe(placeholder);
|
|
});
|
|
|
|
it("should disable input when disabled prop is true", () => {
|
|
wrapper = createWrapper({ disabled: true });
|
|
|
|
const input = wrapper.find("input");
|
|
expect(input.attributes("disabled")).toBeDefined();
|
|
});
|
|
|
|
it("should show map button when showMapButton is true", () => {
|
|
wrapper = createWrapper({ showMapButton: true });
|
|
|
|
const mapButton = wrapper.find(".action-map");
|
|
expect(mapButton.exists()).toBe(true);
|
|
});
|
|
|
|
it("should display existing coordinates in input field", async () => {
|
|
wrapper = createWrapper({ latlng: [37.7749, -122.4194] });
|
|
|
|
await nextTick();
|
|
const input = wrapper.find("input");
|
|
expect(input.element.value).toBe("37.7749, -122.4194");
|
|
});
|
|
});
|
|
|
|
describe("User Input and Validation", () => {
|
|
beforeEach(() => {
|
|
wrapper = createWrapper();
|
|
});
|
|
|
|
it("should emit coordinates when valid input is entered and Enter is pressed", async () => {
|
|
const input = wrapper.find("input");
|
|
|
|
await input.setValue("37.7749, -122.4194");
|
|
await input.trigger("keydown.enter");
|
|
|
|
expect(wrapper.emitted("update:latlng")).toEqual([[[37.7749, -122.4194]]]);
|
|
expect(wrapper.emitted("changed")).toEqual([[{ lat: 37.7749, lng: -122.4194 }]]);
|
|
});
|
|
|
|
it("should not emit coordinates for invalid input", async () => {
|
|
const input = wrapper.find("input");
|
|
|
|
await input.setValue("invalid coordinates");
|
|
await input.trigger("keydown.enter");
|
|
|
|
expect(wrapper.emitted("update:latlng")).toBeFalsy();
|
|
expect(wrapper.emitted("changed")).toBeFalsy();
|
|
});
|
|
|
|
it("should handle various valid coordinate formats", async () => {
|
|
const input = wrapper.find("input");
|
|
|
|
// Test with spaces around comma
|
|
await input.setValue("90, 180");
|
|
await input.trigger("keydown.enter");
|
|
|
|
expect(wrapper.emitted("update:latlng")[0]).toEqual([[90, 180]]);
|
|
});
|
|
});
|
|
|
|
describe("Button Interactions", () => {
|
|
it("should emit open-map event when map button is clicked", async () => {
|
|
wrapper = createWrapper({ showMapButton: true });
|
|
|
|
const mapButton = wrapper.find(".action-map");
|
|
await mapButton.trigger("click");
|
|
|
|
expect(wrapper.emitted("open-map")).toBeTruthy();
|
|
});
|
|
|
|
it("should clear coordinates when clear button is clicked", async () => {
|
|
wrapper = createWrapper({ latlng: [37.7749, -122.4194] });
|
|
|
|
// Wait for component to initialize and coordinateInput to be set
|
|
await nextTick();
|
|
|
|
const clearButton = wrapper.find(".action-clear");
|
|
expect(clearButton.exists()).toBe(true);
|
|
|
|
await clearButton.trigger("click");
|
|
|
|
expect(wrapper.emitted("update:latlng")).toEqual([[[0, 0]]]);
|
|
expect(wrapper.emitted("changed")).toEqual([[{ lat: 0, lng: 0 }]]);
|
|
expect(wrapper.emitted("cleared")).toBeTruthy();
|
|
});
|
|
|
|
it("should show and work with undo button when enabled", async () => {
|
|
wrapper = createWrapper({ enableUndo: true, latlng: [37.7749, -122.4194] });
|
|
|
|
// Wait for component to initialize and coordinateInput to be set
|
|
await nextTick();
|
|
|
|
// Clear coordinates first
|
|
const clearButton = wrapper.find(".action-clear");
|
|
expect(clearButton.exists()).toBe(true);
|
|
await clearButton.trigger("click");
|
|
await nextTick();
|
|
|
|
// Undo button should appear
|
|
const undoButton = wrapper.find(".action-undo");
|
|
expect(undoButton.exists()).toBe(true);
|
|
|
|
// Click undo to restore coordinates
|
|
await undoButton.trigger("click");
|
|
|
|
const latlngEmits = wrapper.emitted("update:latlng");
|
|
|
|
// Last emit should restore original coordinates
|
|
expect(latlngEmits[latlngEmits.length - 1]).toEqual([[37.7749, -122.4194]]);
|
|
});
|
|
});
|
|
|
|
describe("Auto Apply Feature", () => {
|
|
it("should auto apply valid coordinates after debounce delay", async () => {
|
|
wrapper = createWrapper({ autoApply: true, debounceDelay: 500 });
|
|
|
|
const input = wrapper.find("input");
|
|
await input.setValue("37.7749, -122.4194");
|
|
|
|
// Should not emit immediately
|
|
expect(wrapper.emitted("update:latlng")).toBeFalsy();
|
|
|
|
// Fast forward timer
|
|
vi.advanceTimersByTime(500);
|
|
await nextTick();
|
|
|
|
expect(wrapper.emitted("update:latlng")).toEqual([[[37.7749, -122.4194]]]);
|
|
});
|
|
|
|
it("should not auto apply when autoApply is disabled", async () => {
|
|
wrapper = createWrapper({ autoApply: false });
|
|
|
|
const input = wrapper.find("input");
|
|
await input.setValue("37.7749, -122.4194");
|
|
|
|
vi.advanceTimersByTime(1000);
|
|
await nextTick();
|
|
|
|
expect(wrapper.emitted("update:latlng")).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe("Paste Functionality", () => {
|
|
beforeEach(() => {
|
|
wrapper = createWrapper();
|
|
});
|
|
|
|
it("should handle paste with valid coordinates", async () => {
|
|
const input = wrapper.find("input");
|
|
|
|
const pasteEvent = new Event("paste");
|
|
pasteEvent.clipboardData = {
|
|
getData: vi.fn().mockReturnValue("40.7128, -74.0060"),
|
|
};
|
|
|
|
await input.trigger("paste", { clipboardData: pasteEvent.clipboardData });
|
|
|
|
expect(wrapper.emitted("update:latlng")).toEqual([[[40.7128, -74.006]]]);
|
|
expect(wrapper.emitted("changed")).toEqual([[{ lat: 40.7128, lng: -74.006 }]]);
|
|
});
|
|
|
|
it("should handle paste with space-separated coordinates", async () => {
|
|
const input = wrapper.find("input");
|
|
|
|
const pasteEvent = new Event("paste");
|
|
pasteEvent.clipboardData = {
|
|
getData: vi.fn().mockReturnValue("40.7128 -74.0060"),
|
|
};
|
|
|
|
await input.trigger("paste", { clipboardData: pasteEvent.clipboardData });
|
|
|
|
expect(wrapper.emitted("update:latlng")).toEqual([[[40.7128, -74.006]]]);
|
|
});
|
|
});
|
|
|
|
describe("Props Updates", () => {
|
|
it("should update input field when lat/lng props change", async () => {
|
|
wrapper = createWrapper();
|
|
|
|
await wrapper.setProps({ latlng: [40.7128, -74.006] });
|
|
|
|
const input = wrapper.find("input");
|
|
expect(input.element.value).toBe("40.7128, -74.006");
|
|
});
|
|
|
|
it("should clear input field when coordinates are invalid", async () => {
|
|
wrapper = createWrapper({ latlng: [0, 0] });
|
|
|
|
await nextTick();
|
|
const input = wrapper.find("input");
|
|
expect(input.element.value).toBe("");
|
|
});
|
|
});
|
|
});
|