.gitignore and v0.2.0
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { fireEvent, render } from "@testing-library/vue";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import DsLogicTree from "../src/components/data/DsLogicTree.vue";
|
||||
import DsSortableList from "../src/components/data/DsSortableList.vue";
|
||||
import type { DsLogicGroup, DsSortableItem } from "../src/types";
|
||||
|
||||
const sortableItems: DsSortableItem[] = [
|
||||
{ id: "one", label: "Primero" },
|
||||
{ id: "two", label: "Segundo" },
|
||||
{ id: "three", label: "Tercero" },
|
||||
];
|
||||
|
||||
const emptyTree: DsLogicGroup = {
|
||||
id: "root",
|
||||
type: "group",
|
||||
operator: "and",
|
||||
children: [],
|
||||
};
|
||||
|
||||
const fields = [
|
||||
{ label: "Estado", value: "status" },
|
||||
{ label: "Plan", value: "plan" },
|
||||
];
|
||||
|
||||
describe("data tools", () => {
|
||||
it("reorders items with drag-and-drop and emits the movement", async () => {
|
||||
const view = render(DsSortableList, {
|
||||
props: { modelValue: sortableItems, label: "Prioridades" },
|
||||
});
|
||||
const items = view.getAllByRole("listitem");
|
||||
|
||||
await fireEvent.dragStart(items[0] as HTMLElement);
|
||||
await fireEvent.dragOver(items[2] as HTMLElement);
|
||||
await fireEvent.drop(items[2] as HTMLElement);
|
||||
|
||||
const updates = view.emitted()["update:modelValue"] as
|
||||
unknown[][] | undefined;
|
||||
const reordered = updates?.at(-1)?.[0] as DsSortableItem[] | undefined;
|
||||
expect(reordered?.map((item) => item.id)).toEqual(["two", "three", "one"]);
|
||||
expect(view.emitted().reorder?.at(-1)).toEqual([
|
||||
expect.objectContaining({ from: 0, to: 2 }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("offers buttons as an accessible alternative to dragging", async () => {
|
||||
const view = render(DsSortableList, {
|
||||
props: { modelValue: sortableItems, label: "Prioridades" },
|
||||
});
|
||||
|
||||
await fireEvent.click(
|
||||
view.getByRole("button", { name: "Mover abajo: Primero" }),
|
||||
);
|
||||
|
||||
const updates = view.emitted()["update:modelValue"] as
|
||||
unknown[][] | undefined;
|
||||
const reordered = updates?.at(-1)?.[0] as DsSortableItem[] | undefined;
|
||||
expect(reordered?.map((item) => item.id)).toEqual(["two", "one", "three"]);
|
||||
expect(view.getByRole("status").textContent).toContain(
|
||||
"Primero, movido a la posición 2 de 3.",
|
||||
);
|
||||
});
|
||||
|
||||
it("adds a serializable condition to a logic tree", async () => {
|
||||
const view = render(DsLogicTree, {
|
||||
props: { modelValue: emptyTree, fields },
|
||||
});
|
||||
|
||||
await fireEvent.click(
|
||||
view.getByRole("button", { name: "Añadir condición" }),
|
||||
);
|
||||
|
||||
const updates = view.emitted()["update:modelValue"] as
|
||||
unknown[][] | undefined;
|
||||
const tree = updates?.at(-1)?.[0] as DsLogicGroup | undefined;
|
||||
expect(tree?.children).toHaveLength(1);
|
||||
expect(tree?.children[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
type: "condition",
|
||||
field: "status",
|
||||
comparison: "equals",
|
||||
value: "",
|
||||
}),
|
||||
);
|
||||
expect(view.emitted().change?.at(-1)).toEqual([tree]);
|
||||
});
|
||||
|
||||
it("updates operators and removes nested rules", async () => {
|
||||
const tree: DsLogicGroup = {
|
||||
...emptyTree,
|
||||
children: [
|
||||
{
|
||||
id: "status",
|
||||
type: "condition",
|
||||
field: "status",
|
||||
comparison: "equals",
|
||||
value: "active",
|
||||
},
|
||||
],
|
||||
};
|
||||
const operatorView = render(DsLogicTree, {
|
||||
props: { modelValue: tree, fields },
|
||||
});
|
||||
await fireEvent.update(
|
||||
operatorView.getByLabelText("Operador lógico"),
|
||||
"or",
|
||||
);
|
||||
const operatorUpdates = operatorView.emitted()["update:modelValue"] as
|
||||
unknown[][] | undefined;
|
||||
expect(
|
||||
(operatorUpdates?.at(-1)?.[0] as DsLogicGroup | undefined)?.operator,
|
||||
).toBe("or");
|
||||
operatorView.unmount();
|
||||
|
||||
const removeView = render(DsLogicTree, {
|
||||
props: { modelValue: tree, fields },
|
||||
});
|
||||
await fireEvent.click(removeView.getByRole("button", { name: "Eliminar" }));
|
||||
const removeUpdates = removeView.emitted()["update:modelValue"] as
|
||||
unknown[][] | undefined;
|
||||
const updated = removeUpdates?.at(-1)?.[0] as DsLogicGroup | undefined;
|
||||
expect(updated?.children).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import { fireEvent, render } from "@testing-library/vue";
|
||||
import { h } from "vue";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import DsCheckbox from "../src/components/forms/DsCheckbox.vue";
|
||||
import DsRadioGroup from "../src/components/forms/DsRadioGroup.vue";
|
||||
import DsTextInput from "../src/components/forms/DsTextInput.vue";
|
||||
|
||||
describe("form controls", () => {
|
||||
@@ -34,4 +36,43 @@ describe("form controls", () => {
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("associates checkbox descriptions and errors at the same time", () => {
|
||||
const view = render(DsCheckbox, {
|
||||
props: {
|
||||
label: "Acepto",
|
||||
description: "Lee las condiciones.",
|
||||
error: "Debes aceptar.",
|
||||
},
|
||||
});
|
||||
const checkbox = view.getByRole("checkbox");
|
||||
const describedBy = checkbox.getAttribute("aria-describedby") ?? "";
|
||||
expect(describedBy).toContain("description");
|
||||
expect(describedBy).toContain("error");
|
||||
expect(checkbox.getAttribute("aria-invalid")).toBe("true");
|
||||
});
|
||||
|
||||
it("gives radio groups unique descriptions and exposes invalid state", () => {
|
||||
const props = {
|
||||
legend: "Plan",
|
||||
name: "plan",
|
||||
options: [{ label: "Profesional", value: "pro" }],
|
||||
description: "Elige un plan.",
|
||||
error: "Selecciona una opción.",
|
||||
};
|
||||
const view = render({
|
||||
setup: () => () =>
|
||||
h("div", [h(DsRadioGroup, props), h(DsRadioGroup, props)]),
|
||||
});
|
||||
const groups = view.getAllByRole("group");
|
||||
const describedBy = groups.map((group) =>
|
||||
group.getAttribute("aria-describedby"),
|
||||
);
|
||||
expect(describedBy[0]).not.toBe(describedBy[1]);
|
||||
expect(describedBy[0]).toContain("description");
|
||||
expect(describedBy[0]).toContain("error");
|
||||
expect(view.getAllByRole("radio")[0]?.getAttribute("aria-invalid")).toBe(
|
||||
"true",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@ describe("public API", () => {
|
||||
expect(designSystem.DsTextInput).toBeDefined();
|
||||
expect(designSystem.DsTabs).toBeDefined();
|
||||
expect(designSystem.DsDialog).toBeDefined();
|
||||
expect(designSystem.DsSortableList).toBeDefined();
|
||||
expect(designSystem.DsLogicTree).toBeDefined();
|
||||
expect(designSystem.DesignSystem).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { fireEvent, render } from "@testing-library/vue";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { h } from "vue";
|
||||
import DsTabs from "../src/components/navigation/DsTabs.vue";
|
||||
|
||||
describe("DsTabs", () => {
|
||||
@@ -29,4 +30,26 @@ describe("DsTabs", () => {
|
||||
});
|
||||
expect(view.emitted()["update:modelValue"]?.at(-1)).toEqual(["billing"]);
|
||||
});
|
||||
|
||||
it("keeps tab and panel relationships unique across instances", () => {
|
||||
const props = {
|
||||
label: "Cuenta",
|
||||
modelValue: "profile",
|
||||
items: [{ id: "profile", label: "Perfil" }],
|
||||
};
|
||||
const view = render({
|
||||
setup: () => () =>
|
||||
h("div", [
|
||||
h(DsTabs, props, { profile: () => "Perfil" }),
|
||||
h(DsTabs, props, { profile: () => "Perfil" }),
|
||||
]),
|
||||
});
|
||||
const [firstTab, secondTab] = view.getAllByRole("tab");
|
||||
const [firstPanel, secondPanel] = view.getAllByRole("tabpanel");
|
||||
|
||||
expect(firstTab?.id).not.toBe(secondTab?.id);
|
||||
expect(firstPanel?.id).not.toBe(secondPanel?.id);
|
||||
expect(firstTab?.getAttribute("aria-controls")).toBe(firstPanel?.id);
|
||||
expect(firstPanel?.getAttribute("aria-labelledby")).toBe(firstTab?.id);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user