@shopware-ag/meteor-component-library
Version:
The meteor component library is a Vue component library developed by Shopware. It is based on the [Meteor Design System](https://shopware.design/).
1,283 lines (1,051 loc) • 40.7 kB
text/typescript
import { mount } from "@vue/test-utils";
import MtDataTable, { type ColumnDefinition } from "./mt-data-table.vue";
import MtDataTableFixtures from "./mt-data-table.fixtures.json";
import flushPromises from "flush-promises";
import { get } from "@/utils/object";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { useI18n } from "vue-i18n";
// Mock "useDebounceFn" from "@vueuse/core"
vi.mock("@vueuse/core", () => ({
useDebounceFn: (method: () => void) => {
return method;
},
}));
vi.mock("vue-i18n", () => ({
useI18n: vi.fn(() => {
return {
t: (tKey: string) => tKey,
};
}),
}));
const columnsFixture: ColumnDefinition[] = [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 200,
},
{
label: "Manufacturer",
property: "manufacturer.name",
renderer: "text",
position: 100,
},
{
label: "Active",
property: "active",
renderer: "badge",
rendererOptions: {
renderItemBadge: (data, columnDefinition) => {
// @ts-expect-error
const value = get(data, columnDefinition.property);
return value
? {
variant: "positive",
label: "Active",
}
: {
variant: "critical",
label: "Inactive",
};
},
},
position: 200,
width: 123,
allowResize: false,
},
{
label: "Price",
property: "price",
renderer: "price",
position: 300,
rendererOptions: {
currencyId: "b7d2554b0ce847cd82f3ac9bd1c0dfca",
currencyISOCode: "EUR",
source: "net",
},
},
{
label: "Stock",
property: "stock",
renderer: "number",
position: 400,
width: 100,
},
{
label: "Available",
property: "available",
renderer: "number",
position: 500,
width: 100,
},
];
const DEFAULT_MIN_WIDTH = "100px";
// mock resizeOvserver
global.ResizeObserver = class ResizeObserver {
observe() {
// do nothing
}
unobserve() {
// do nothing
}
disconnect() {
// do nothing
}
};
function createWrapper(options?: { slots?: Record<string, string> }) {
return mount(MtDataTable, {
attachTo: document.body,
props: {
dataSource: MtDataTableFixtures,
columns: columnsFixture,
title: "Data table",
subtitle: "This is the subline",
currentPage: 1,
paginationTotalItems: 182,
paginationLimit: 25,
},
global: {
mocks: {
$t: (v: string) => v,
},
stubs: {
"mt-icon": true,
teleport: true,
},
},
...options,
});
}
describe("mt-data-table", () => {
beforeEach(() => {
// @ts-expect-error - mock was set via jest
if (console.error.mockRestore) {
// @ts-expect-error - mock was set via jest
console.error.mockRestore();
}
// reset global styling
document.body.style.cursor = "";
// @ts-expect-error
if (window.removeEventListener.mockReset) {
// @ts-expect-error
window.removeEventListener.mockReset();
}
});
afterEach(async () => {
await flushPromises();
});
it("should render the component", () => {
const wrapper = createWrapper();
expect(wrapper.vm).toBeTruthy();
});
describe("should throw an console error if columns aren't correctly defined", () => {
it("missing label", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
// @ts-expect-error - validator check
{
property: "name",
renderer: "text",
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("missing property", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
// @ts-expect-error - validator check
{
label: "name",
renderer: "text",
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("missing renderer", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
// @ts-expect-error - validator check
{
label: "name",
property: "name",
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("missing position", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
// @ts-expect-error - validator check
{
label: "name",
property: "name",
renderer: "text",
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("wrong label property", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
// @ts-expect-error - validator check
label: 1,
property: "name",
renderer: "text",
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("wrong property property", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
// @ts-expect-error - validator check
property: 1,
renderer: "text",
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("wrong renderer property", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "name",
property: "name",
// @ts-expect-error - validator check
renderer: 1,
position: 0,
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
it("wrong position property", async () => {
// spy for console.error calls
vi.spyOn(console, "warn").mockImplementation(() => {});
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
// @ts-expect-error - validator check
position: "0",
width: 200,
},
],
});
// @ts-expect-error - mock was set from jest
expect(console.warn.mock.calls[0][0]).toContain(
`Invalid prop: custom validator check failed for prop "columns"`,
);
});
});
describe("should render the table header cells correctly", () => {
it("should render table header cells everytime with 'fit-content' in the 'max-width' property", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 200,
},
],
});
const firstColumnHeader = wrapper.findAll("th").at(0);
expect(firstColumnHeader?.attributes().style).toContain("max-width: fit-content");
});
it("should render table header cells with default minimum column width and auto main width when given width is undefined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
},
],
});
const firstColumnHeader = wrapper.findAll("th").at(0);
expect(firstColumnHeader?.attributes().style).toContain(`min-width: ${DEFAULT_MIN_WIDTH}`);
expect(firstColumnHeader?.attributes().style).toContain("width: auto");
});
it("should render table header cells with defined column width when width is defined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 789,
},
],
});
const firstColumnHeader = wrapper.findAll("th").at(0);
expect(firstColumnHeader?.attributes().style).toContain("min-width: 789px");
expect(firstColumnHeader?.attributes().style).toContain("width: 789px");
});
});
describe("should render the table data cells correctly", () => {
it("should render table data cells with 'max-width' to 'auto' when 'cellWrap' is 'normal'", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 789,
cellWrap: "normal",
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain("max-width: auto");
});
it("should render table data cells with 'max-width' to 'XXXpx' when 'cellWrap' is 'nowrap' and 'width' is defined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 789,
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain("max-width: 789px");
});
it("should render table data cells with 'max-width' to fallback width when 'cellWrap' is 'nowrap' and 'width' is undefined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain(`max-width: ${DEFAULT_MIN_WIDTH}`);
});
it("should render table data cells with nowrap white-space when cellWrap is undefined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain("white-space: nowrap");
});
it("should render table data cells with 'normal' white-space when cellWrap is 'normal'", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
cellWrap: "normal",
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain("white-space: normal");
});
it("should render table data cells with default minimum column width when width is undefined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain(`min-width: ${DEFAULT_MIN_WIDTH}`);
});
it("should render table data cells with defined column width when width is defined", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
renderer: "text",
position: 0,
width: 789,
},
],
});
const firstColumnData = wrapper.findAll("td").at(0);
expect(firstColumnData?.attributes().style).toContain("min-width: 789px");
});
it("should not render table cell when disable context menu action and settings table action", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
disableEdit: true,
disableDelete: true,
disableSettingsTable: true,
});
expect(wrapper.find(".mt-data-table__table-context-button").exists()).toBeFalsy();
expect(wrapper.find(".mt-data-table__table-settings-button").exists()).toBeFalsy();
});
it("should render table cell when using slot column", async () => {
const wrapper = createWrapper({
slots: {
"column-name": "My custom name",
},
});
await wrapper.setProps({
...wrapper.props(),
});
const dataCellName = wrapper.findAll('[data-cell-column-property="name"]');
expect(dataCellName[0].html()).toContain(`My custom name`);
});
it("should render table data with disabled row selection", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
allowRowSelection: true,
disableRowSelect: ["4f683593-13f1-4767-91c6-8e154d68a22d"], // product id of "Awesome Concrete Chair"
});
const rows = wrapper.findAll("tr");
const disabledRowIndex = rows.findIndex((row) =>
row.text().includes("Awesome Concrete Chair"),
);
const allowRowIndex = rows.findIndex((row) => !row.text().includes("Awesome Concrete Chair"));
expect(
rows[disabledRowIndex]
.find(".mt-data-table__table-select-row .mt-field--checkbox .is--disabled")
.exists(),
).toBeTruthy();
expect(
rows[allowRowIndex]
.find(".mt-data-table__table-select-row .mt-field--checkbox .is--disabled")
.exists(),
).toBeFalsy();
});
});
it("should render table data cells with an image inside", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columns: [
{
label: "Name",
property: "name",
previewImage: "imageURL",
renderer: "text",
position: 0,
},
],
});
const image = wrapper.find("img");
expect(image.exists()).toBeTruthy();
});
describe("should render the card props correctly", () => {
it("should render the title", () => {
const wrapper = createWrapper();
const title = wrapper.find(".mt-card__title");
expect(title.exists()).toBeTruthy();
expect(title.text()).toBe("Data table");
});
it("should render the subtitle", () => {
const wrapper = createWrapper();
const subtitle = wrapper.find(".mt-card__subtitle");
expect(subtitle.exists()).toBeTruthy();
expect(subtitle.text()).toBe("This is the subline");
});
it("should not render the title", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
title: undefined,
});
const title = wrapper.find(".mt-card__title");
expect(title.exists()).toBeFalsy();
});
it("should not render the subtitle", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
subtitle: undefined,
});
const subtitle = wrapper.find(".mt-card__subtitle");
expect(subtitle.exists()).toBeFalsy();
});
});
describe("should render the general props correctly", () => {
it("should not render the reload button", async () => {
const wrapper = createWrapper();
const reloadButton = wrapper.find('.mt-button[aria-label="reload-data"]');
expect(reloadButton.exists()).toBeFalsy();
});
it("should render the reload button", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
enableReload: true,
});
const reloadButton = wrapper.find('.mt-button[aria-label="reload-data"]');
expect(reloadButton.exists()).toBeTruthy();
});
});
describe("should execute the functionalities correctly", () => {
it("should emit the reload on event on clicking the reload button", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
enableReload: true,
});
const reloadButton = wrapper.find('.mt-button[aria-label="reload-data"]');
expect(wrapper.emitted().reload).toBeFalsy();
await reloadButton.trigger("click");
expect(wrapper.emitted().reload).toBeTruthy();
});
});
describe("should have a correct column resizing behaviour", () => {
it("should render the resizable div", async () => {
const wrapper = createWrapper();
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const tableHeadCellManufacturer = wrapper.find(
'[data-header-column-property="manufacturer.name"]',
);
const tableHeadCellPrice = wrapper.find('[data-header-column-property="price"]');
expect(
tableHeadCellName.find(".mt-data-table__table-head-resizable-after").exists(),
).toBeTruthy();
expect(
tableHeadCellManufacturer.find(".mt-data-table__table-head-resizable-after").exists(),
).toBeTruthy();
expect(
tableHeadCellPrice.find(".mt-data-table__table-head-resizable-after").exists(),
).toBeTruthy();
});
it("should not render the resizable div when allowedResize is set to false", async () => {
const wrapper = createWrapper();
const tableHeadCellActive = wrapper.find('[data-header-column-property="active"]');
expect(
tableHeadCellActive.find(".mt-data-table__table-head-resizable-after").exists(),
).toBeFalsy();
});
it("should make all columns fixed width when start resizing", async () => {
const wrapper = createWrapper();
// these columns should have fixed width later
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const tableHeadCellManufacturer = wrapper.find(
'[data-header-column-property="manufacturer.name"]',
);
// this column should not change their width
const tableHeadCellActive = wrapper.find('[data-header-column-property="active"]');
// check header columns before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
expect(tableHeadCellManufacturer.attributes().style).toContain(
`width: auto; min-width: ${DEFAULT_MIN_WIDTH}; max-width: fit-content;`,
);
expect(tableHeadCellActive.attributes().style).toContain(
"width: 123px; min-width: 123px; max-width: fit-content;",
);
// check data cell columns before resizing
const dataCellName = wrapper.findAllComponents('[data-cell-column-property="name"]');
const dataCellManufacturer = wrapper.findAll(
'[data-cell-column-property="manufacturer.name"]',
);
const dataCellActive = wrapper.findAll('[data-cell-column-property="active"]');
dataCellName.forEach((dataCell) => {
expect(dataCell.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: 200px;",
);
});
dataCellManufacturer.forEach((dataCell) => {
expect(dataCell.attributes().style).toContain(
"width: auto; min-width: 100px; max-width: 100px;",
);
});
dataCellActive.forEach((dataCell) => {
expect(dataCell.attributes().style).toContain(
"width: 123px; min-width: 123px; max-width: 123px;",
);
});
// TRIGGER RESIZING
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ width: 200 });
// @ts-expect-error - only needed values are set
tableHeadCellManufacturer.element.getBoundingClientRect = () => ({ width: 100 });
// @ts-expect-error - only needed values are set
tableHeadCellActive.element.getBoundingClientRect = () => ({ width: 123 });
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
resizableDiv.trigger("mousedown");
// check header columns after resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// should now be fixed width and not auto
expect(tableHeadCellManufacturer.attributes().style).toContain(
"width: 100px; min-width: 100px; max-width: fit-content;",
);
expect(tableHeadCellActive.attributes().style).toContain(
"width: 123px; min-width: 123px; max-width: fit-content;",
);
});
it("should set the cursor globally to col-resize when start resizing", async () => {
const wrapper = createWrapper();
// check cursor before resizing
expect(document.body.style.cursor).toBe("");
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// check cursor after resizing
expect(document.body.style.cursor).toBe("col-resize");
});
it("should remove the global col-resize cursor when stopping resizing", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// check cursor after resizing
expect(document.body.style.cursor).toBe("col-resize");
// TRIGGER STOP RESIZING
await window.dispatchEvent(new Event("mouseup"));
// check cursor after resizing
expect(document.body.style.cursor).toBe("");
});
it('should add class "--no-transition" to table when resizing', async () => {
const wrapper = createWrapper();
// check if class not exists before resizing
expect(wrapper.find("table").classes()).not.toContain("--no-transition");
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// check class after resizing
expect(wrapper.find("table").classes()).toContain("--no-transition");
});
it('should remove class "--no-transition" to table when finishing resizing', async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// check if class not exists before resizing
expect(wrapper.find("table").classes()).not.toContain("--no-transition");
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// check class after resizing
expect(wrapper.find("table").classes()).toContain("--no-transition");
// TRIGGER STOP RESIZING
await window.dispatchEvent(new Event("mouseup"));
// check class after resizing
expect(wrapper.find("table").classes()).not.toContain("--no-transition");
});
it("should stop and prevent default event behaviour like propagation and preventDefault in mouseMove handler", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
vi.spyOn(mouseMoveEvent, "preventDefault").mockImplementation(() => {});
vi.spyOn(mouseMoveEvent, "stopPropagation").mockImplementation(() => {});
await window.dispatchEvent(mouseMoveEvent);
// check if preventDefault and stopPropagation was called
expect(mouseMoveEvent.preventDefault).toHaveBeenCalledWith();
expect(mouseMoveEvent.stopPropagation).toHaveBeenCalledWith();
});
it("should set the correct widths for the current column cells when the mouse is moving", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 230;
await window.dispatchEvent(mouseMoveEvent);
// check if width and height are set correctly
expect(tableHeadCellName.attributes().style).toContain(
"width: 155px; min-width: 155px; max-width: fit-content;",
);
const dataCellName = wrapper.findAllComponents('[data-cell-column-property="name"]');
dataCellName.forEach((dataCell) => {
expect(dataCell.attributes().style).toContain(
"width: 155px; min-width: 155px; max-width: 155px;",
);
});
});
it("should add the correct padding for the current column cells when shrinking the column more than at the beginning", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ width: 200, left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 230;
await window.dispatchEvent(mouseMoveEvent);
// check if padding is set correctly to dataTable
expect(wrapper.find("table").attributes().style).toContain("padding-right: 45px;");
});
it("should add no padding for the current column cells when enlarging the column more than at the beginning", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ width: 200, left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 630;
await window.dispatchEvent(mouseMoveEvent);
// check if no padding was set because the column gets enlarged
expect(wrapper.find("table").attributes().style).toBeUndefined();
});
it("should remove the padding when stopping resizing", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ width: 200, left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 230;
await window.dispatchEvent(mouseMoveEvent);
// check if padding is set correctly to dataTable
expect(wrapper.find("table").attributes().style).toContain("padding-right: 45px;");
// TRIGGER STOP RESIZING
await window.dispatchEvent(new Event("mouseup"));
// check if padding was removed correctly
expect(wrapper.find("table").attributes().style).toBe("");
});
it("should save the new width to the property columnChanges", async () => {
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 230;
await window.dispatchEvent(mouseMoveEvent);
// TRIGGER STOP RESIZING
await window.dispatchEvent(new Event("mouseup"));
// check if columnChanges prop was updated correctly
expect(wrapper.props().columnChanges).toStrictEqual({ name: { width: 155 } });
});
it("should load the new widths from the property columnChanges when they were defined beforehand", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
columnChanges: {
name: { width: 179 },
},
});
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 179px; min-width: 179px; max-width: fit-content;",
);
});
it("should remove all handlers when stopping resizing", async () => {
const removeEventListenerSpy = vi.spyOn(window, "removeEventListener");
const wrapper = createWrapper();
// get the resizable div
const tableHeadCellName = wrapper.find('[data-header-column-property="name"]');
const resizableDiv = tableHeadCellName.find(".mt-data-table__table-head-resizable-after");
// @ts-expect-error - only needed values are set
tableHeadCellName.element.getBoundingClientRect = () => ({ left: 75 });
// check if width and height are set before resizing
expect(tableHeadCellName.attributes().style).toContain(
"width: 200px; min-width: 200px; max-width: fit-content;",
);
// TRIGGER RESIZING
await resizableDiv.trigger("mousedown");
// TRIGGER MOUSE MOVE EVENT
const mouseMoveEvent = new Event("mousemove");
// @ts-expect-error - only needed values are set
mouseMoveEvent.pageX = 230;
await window.dispatchEvent(mouseMoveEvent);
// TRIGGER STOP RESIZING
await window.dispatchEvent(new Event("mouseup"));
// check if all handlers were removed
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2);
expect(removeEventListenerSpy).toHaveBeenCalledWith("mousemove", expect.any(Function));
expect(removeEventListenerSpy).toHaveBeenCalledWith("mouseup", expect.any(Function));
});
});
describe("should have correct search funcitonalitities", () => {
it("should have a search input field", () => {
const wrapper = createWrapper();
expect(wrapper.find(".mt-search").exists()).toBeTruthy();
});
it("should not render the search input field", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
disableSearch: true,
});
expect(wrapper.find(".mt-search").exists()).toBeFalsy();
});
it("should use the search value from prop", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
searchValue: "My initital search value",
});
const searchInput = wrapper.find(".mt-search input");
// @ts-expect-error - inputElement has value
expect(searchInput.element.value).toBe("My initital search value");
});
it("should emit the search value", async () => {
const wrapper = createWrapper();
const searchInput = wrapper.find(".mt-search input");
await searchInput.setValue("My new search value");
await searchInput.trigger("change");
expect(wrapper.emitted()["search-value-change"]).toBeTruthy();
// @ts-expect-error - only needed values are set
expect(wrapper.emitted()["search-value-change"][0][0]).toBe("My new search value");
});
});
describe("should handle the mt-data-table-settings events correctly", () => {
it("should reset all changes", async () => {
const wrapper = createWrapper();
// simulate some column changes
await wrapper.setProps({
...wrapper.props(),
columnChanges: {
active: { visible: false },
available: { width: 987 },
},
});
expect(wrapper.props().columnChanges).toStrictEqual({
active: { visible: false },
available: { width: 987 },
});
const MtDataTableSettings = wrapper.findComponent({ name: "MtDataTableSettings" });
await MtDataTableSettings.vm.$emit("reset-all-changes");
await wrapper.vm.$nextTick();
// check if all changes were resetted
expect(wrapper.props().columnChanges).toStrictEqual({
active: {},
available: {},
});
});
it("should change the column order", async () => {
const wrapper = createWrapper();
expect(wrapper.props().columnChanges).toStrictEqual({});
const MtDataTableSettings = wrapper.findComponent({ name: "MtDataTableSettings" });
await MtDataTableSettings.vm.$emit("change-column-order", {
itemId: "price",
dropId: "name",
dropZone: "after",
});
await wrapper.vm.$nextTick();
// check if order was updated correctly
expect(wrapper.props().columnChanges).toStrictEqual({
name: { position: 0 },
price: { position: 100 },
"manufacturer.name": { position: 200 },
active: { position: 300 },
stock: { position: 400 },
available: { position: 500 },
});
});
it("should change the column visibility", async () => {
const wrapper = createWrapper();
expect(wrapper.props().columnChanges).toStrictEqual({});
const MtDataTableSettings = wrapper.findComponent({ name: "MtDataTableSettings" });
await MtDataTableSettings.vm.$emit("change-column-visibility", "active", false);
await wrapper.vm.$nextTick();
// check if order was updated correctly
expect(wrapper.props().columnChanges).toStrictEqual({
active: { visible: false },
});
});
it("should disable settings table", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
disableSettingsTable: true,
});
expect(wrapper.find(".mt-data-table-settings").exists()).toBeFalsy();
});
it("should not disable settings table when there is an additonal context menu", async () => {
const wrapper = createWrapper();
await wrapper.setProps({
...wrapper.props(),
disableSettingsTable: true,
disableDelete: true,
disableEdit: true,
additionalContextButtons: [
{
label: "Set Price",
key: "set-price",
},
],
});
const contextButton = wrapper.find(
".mt-data-table__table-context-button .mt-context-button button",
);
expect(contextButton.exists()).toBeTruthy();
await contextButton.trigger("click");
const menuItem: any = document.querySelector(".mt-context-menu-item");
menuItem?.click();
expect((wrapper.emitted("context-select")?.[0]?.[0] as any)?.key).toEqual("set-price");
});
});
});