Add a persistent recursive expand/collapse control above collected invoices and keep order-item quantity and price in the canonical editable field layout.
190 lines
6.3 KiB
JavaScript
190 lines
6.3 KiB
JavaScript
// @vitest-environment jsdom
|
|
|
|
import { flushPromises, mount } from "@vue/test-utils";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import BuefyTree from "@/components/buefy/tree/BuefyTree.vue";
|
|
|
|
vi.mock("buefy", () => ({
|
|
BCheckbox: {
|
|
name: "BCheckbox",
|
|
props: {
|
|
modelValue: Boolean,
|
|
indeterminate: Boolean,
|
|
disabled: Boolean,
|
|
},
|
|
emits: ["update:modelValue"],
|
|
template: `
|
|
<button
|
|
class="b-checkbox-stub"
|
|
type="button"
|
|
:disabled="disabled"
|
|
:data-checked="modelValue"
|
|
:data-indeterminate="indeterminate"
|
|
@click="$emit('update:modelValue', !modelValue)"
|
|
></button>
|
|
`,
|
|
},
|
|
}));
|
|
|
|
describe("BuefyTree checkbox selection", () => {
|
|
it("uses non-selectable checkable categories as lazy select-all branch controllers", async () => {
|
|
const load = vi.fn(async () => [
|
|
{ id: "order:70128", label: "Ordre #70128", selectable: true, isLeaf: true },
|
|
{ id: "order:70129", label: "Ordre #70129", selectable: true, isLeaf: true },
|
|
]);
|
|
const wrapper = mount(BuefyTree, {
|
|
props: {
|
|
data: [
|
|
{
|
|
id: "category:orders",
|
|
label: "Orders",
|
|
selectable: false,
|
|
checkable: true,
|
|
isLeaf: false,
|
|
},
|
|
],
|
|
selectionMode: "checkbox",
|
|
lazy: true,
|
|
load,
|
|
},
|
|
});
|
|
|
|
await wrapper.find(".b-checkbox-stub").trigger("click");
|
|
await flushPromises();
|
|
|
|
expect(load).toHaveBeenCalledTimes(1);
|
|
expect(wrapper.emitted("update:checkedKeys")?.at(-1)?.[0]).toEqual(["order:70128", "order:70129"]);
|
|
|
|
await wrapper.find(".b-checkbox-stub").trigger("click");
|
|
await flushPromises();
|
|
|
|
expect(load).toHaveBeenCalledTimes(1);
|
|
expect(wrapper.emitted("update:checkedKeys")?.at(-1)?.[0]).toEqual([]);
|
|
});
|
|
|
|
it("renders progressive batches while selecting every logical child", async () => {
|
|
const children = Array.from({ length: 1000 }, (_, index) => ({
|
|
id: `order:${index + 1}`,
|
|
label: `Order ${index + 1}`,
|
|
selectable: true,
|
|
isLeaf: true,
|
|
}));
|
|
const wrapper = mount(BuefyTree, {
|
|
props: {
|
|
data: [{ id: "category:orders", label: "Orders", selectable: false, checkable: true, isLeaf: false }],
|
|
selectionMode: "checkbox",
|
|
lazy: true,
|
|
progressiveBatchSize: 50,
|
|
loadMoreLabel: "Show {count} more",
|
|
load: vi.fn(async () => children),
|
|
},
|
|
});
|
|
|
|
await wrapper.find(".b-tree-node-toggle").trigger("click");
|
|
await flushPromises();
|
|
expect(wrapper.findAll('[data-node-key^="order:"]')).toHaveLength(50);
|
|
expect(wrapper.get(".b-tree-load-more__button").text()).toContain("Show 50 more");
|
|
|
|
await wrapper.get(".b-tree-load-more__button").trigger("click");
|
|
expect(wrapper.findAll('[data-node-key^="order:"]')).toHaveLength(100);
|
|
|
|
await wrapper.find(".b-checkbox-stub").trigger("click");
|
|
await flushPromises();
|
|
expect(wrapper.emitted("update:checkedKeys")?.at(-1)?.[0]).toHaveLength(1000);
|
|
expect(wrapper.findAll('[data-node-key^="order:"]')).toHaveLength(100);
|
|
});
|
|
|
|
it("recursively expands lazy branches, reveals every child, and reuses the cache after collapse", async () => {
|
|
let activeLoads = 0;
|
|
let maxActiveLoads = 0;
|
|
const load = vi.fn(async (node) => {
|
|
activeLoads += 1;
|
|
maxActiveLoads = Math.max(maxActiveLoads, activeLoads);
|
|
await Promise.resolve();
|
|
activeLoads -= 1;
|
|
|
|
if (node.id === "root") {
|
|
return [
|
|
{ id: "branch:a", label: "Branch A", isLeaf: false },
|
|
{ id: "branch:b", label: "Branch B", isLeaf: false },
|
|
];
|
|
}
|
|
if (node.id === "branch:a") {
|
|
return [
|
|
{ id: "leaf:a1", label: "Leaf A1", isLeaf: true },
|
|
{ id: "leaf:a2", label: "Leaf A2", isLeaf: true },
|
|
];
|
|
}
|
|
if (node.id === "branch:b") {
|
|
return [{ id: "branch:b1", label: "Branch B1", isLeaf: false }];
|
|
}
|
|
if (node.id === "branch:b1") {
|
|
return [{ id: "leaf:b1", label: "Leaf B1", isLeaf: true }];
|
|
}
|
|
return [];
|
|
});
|
|
const wrapper = mount(BuefyTree, {
|
|
props: {
|
|
data: [{ id: "root", label: "Root", isLeaf: false }],
|
|
lazy: true,
|
|
load,
|
|
progressiveBatchSize: 1,
|
|
},
|
|
});
|
|
|
|
const [firstResult, duplicateResult] = await Promise.all([wrapper.vm.expandAll(), wrapper.vm.expandAll()]);
|
|
await flushPromises();
|
|
|
|
expect(firstResult).toEqual(duplicateResult);
|
|
expect(firstResult.cancelled).toBe(false);
|
|
expect(firstResult.failedKeys).toEqual([]);
|
|
expect(firstResult.expandedKeys).toEqual(["root", "branch:a", "branch:b", "branch:b1"]);
|
|
expect(load).toHaveBeenCalledTimes(4);
|
|
expect(maxActiveLoads).toBeGreaterThan(1);
|
|
expect(maxActiveLoads).toBeLessThanOrEqual(4);
|
|
expect(wrapper.findAll('[data-node-key^="branch:"]')).toHaveLength(3);
|
|
expect(wrapper.findAll('[data-node-key^="leaf:"]')).toHaveLength(3);
|
|
|
|
wrapper.vm.collapseAll();
|
|
await flushPromises();
|
|
expect(wrapper.findAll('[data-node-key^="branch:"]')).toHaveLength(0);
|
|
|
|
await wrapper.vm.expandAll();
|
|
await flushPromises();
|
|
expect(load).toHaveBeenCalledTimes(4);
|
|
expect(wrapper.findAll('[data-node-key^="leaf:"]')).toHaveLength(3);
|
|
});
|
|
|
|
it("continues expanding sibling branches when one lazy branch fails", async () => {
|
|
const load = vi.fn(async (node) => {
|
|
if (node.id === "root") {
|
|
return [
|
|
{ id: "branch:good", label: "Good", isLeaf: false },
|
|
{ id: "branch:bad", label: "Bad", isLeaf: false },
|
|
];
|
|
}
|
|
if (node.id === "branch:bad") {
|
|
throw new Error("Branch failed");
|
|
}
|
|
return [{ id: "leaf:good", label: "Good leaf", isLeaf: true }];
|
|
});
|
|
const wrapper = mount(BuefyTree, {
|
|
props: {
|
|
data: [{ id: "root", label: "Root", isLeaf: false }],
|
|
lazy: true,
|
|
load,
|
|
},
|
|
});
|
|
|
|
const result = await wrapper.vm.expandAll();
|
|
await flushPromises();
|
|
|
|
expect(result.cancelled).toBe(false);
|
|
expect(result.failedKeys).toEqual(["branch:bad"]);
|
|
expect(result.expandedKeys).toEqual(["root", "branch:good", "branch:bad"]);
|
|
expect(wrapper.emitted("load-error")?.[0]?.[0]).toEqual(new Error("Branch failed"));
|
|
expect(wrapper.get('[data-node-key="leaf:good"]').text()).toContain("Good leaf");
|
|
expect(wrapper.get('[data-node-key="branch:bad"]').classes()).toContain("has-load-error");
|
|
});
|
|
});
|