Files
UI/packages/core/table-core/scripts/smoke.mjs
T

197 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
createTable,
filterPlugin,
paginationPlugin,
selectionPlugin,
sortPlugin,
} from '../dist/index.js';
const users = [
{ id: 1, name: 'Charlie', age: 35, city: 'Shenzhen' },
{ id: 2, name: 'Alice', age: 28, city: 'Beijing' },
{ id: 3, name: 'Bob', age: 31, city: 'Shanghai' },
{ id: 4, name: 'Dave', age: 22, city: 'Beijing' },
{ id: 5, name: 'Eve', age: 29, city: 'Shanghai' },
];
const table = createTable({
data: users,
columns: [
{ key: 'name', header: 'Name', sortable: true },
{ key: 'age', header: 'Age', sortable: true },
{ key: 'city', header: 'City' },
],
rowKey: 'id',
plugins: [filterPlugin(), sortPlugin(), paginationPlugin(), selectionPlugin()],
initialState: { pagination: { page: 1, pageSize: 2 } },
});
/*** 断言工具:失败即退出 ***/
const assert = (cond, msg) => {
if (!cond) {
console.error('FAIL:', msg);
process.exit(1);
}
console.log('ok -', msg);
};
table.dispatch({ type: 'toggleSort', key: 'name' }); // 按名称升序
assert(
JSON.stringify(table.getVisibleRows().rows.map((r) => r.name)) ===
JSON.stringify(['Alice', 'Bob']),
'sort by name asc + page 1 of size 2',
);
table.dispatch({ type: 'setFilter', key: 'city', value: 'shang' }); // 筛选上海
const filtered = table.getVisibleRows();
assert(
JSON.stringify(filtered.rows.map((r) => r.name)) ===
JSON.stringify(['Bob', 'Eve']) &&
filtered.total === 2,
'filter city~sh keeps Shanghai rows, total=2',
);
assert(table.getPageInfo().totalPages === 1, 'totalPages computed from filtered total');
table.dispatch({ type: 'clearFilters' }); // 清筛选回到全量
table.dispatch({ type: 'setPage', page: 2 });
assert(
JSON.stringify(table.getVisibleRows().rows.map((r) => r.name)) ===
JSON.stringify(['Charlie', 'Dave']),
'page 2 after clearing filter',
);
assert(table.getPageInfo().page === 2 && table.getPageInfo().totalPages === 3, 'page info total=3 pages');
table.dispatch({ type: 'toggleSelect', key: 3 }); // 勾选行 3
assert(JSON.stringify(table.getState().selection) === JSON.stringify([3]), 'toggleSelect adds rowKey');
table.selectAllVisible(); // 全选当前页(替换语义)
assert(
JSON.stringify(table.getState().selection) === JSON.stringify([1, 4]),
'selectAllVisible replaces selection with visible rowKeys',
);
table.dispatch({ type: 'setPage', page: 99 }); // 越界页码
const clamped = table.getVisibleRows();
assert(
table.getPageInfo().page === 3 && clamped.rows.length === 1,
'page clamped to totalPages',
);
const before = table.getVisibleRows(); // memo 基线
table.dispatch({ type: 'clearSelection' }); // 无关状态变更
assert(table.getVisibleRows() === before, 'derive stages memoized across unrelated state changes');
// —— 补充不变量 ——
const fresh = createTable({
data: users,
columns: [{ key: 'name', header: 'Name', sortable: true }],
rowKey: 'id',
plugins: [paginationPlugin()],
initialState: { pagination: { page: 2, pageSize: 2 } },
});
assert(
fresh.getPageInfo().page === 2 && fresh.getPageInfo().pageSize === 2,
'getPageInfo 无需先取可见行即返回 initialState 生效值',
);
assert(fresh.getPageInfo().totalPages === 3, 'getPageInfo 的 totalPages 由上游 total 推导');
const marks = createTable({
data: users,
columns: [{ key: 'name', header: 'Name' }, { key: 'city', header: 'City' }],
rowKey: 'id',
plugins: [filterPlugin(), paginationPlugin(), selectionPlugin()],
initialState: { pagination: { page: 1, pageSize: 2 } },
});
marks.selectAllVisible(); // 选中第 1 页的 id 1 / 2
marks.dispatch({ type: 'setFilter', key: 'city', value: 'Beijing' }); // 可见行整体换掉
assert(
JSON.stringify(marks.getState().selection) === JSON.stringify([1, 2]),
'selection 以 rowKey 记账,筛选换掉可见行后仍保留',
);
const toggle = createTable({
data: users,
columns: [{ key: 'age', header: 'Age', sortable: true }],
rowKey: 'id',
plugins: [sortPlugin()],
});
toggle.dispatch({ type: 'toggleSort', key: 'age' });
toggle.dispatch({ type: 'toggleSort', key: 'age' });
toggle.dispatch({ type: 'toggleSort', key: 'age' });
assert(toggle.getState().sorting.length === 0, '同一列连点三次回到无序(asc → desc → 取消)');
const multi = createTable({
data: users,
columns: [{ key: 'city', header: 'City' }, { key: 'age', header: 'Age', sortable: true }],
rowKey: 'id',
plugins: [sortPlugin({ multi: true })],
});
multi.dispatch({ type: 'toggleSort', key: 'city', multi: true });
multi.dispatch({ type: 'toggleSort', key: 'age', multi: true });
assert(multi.getState().sorting.length === 2, 'multi 排序保留两列规则');
assert(
JSON.stringify(multi.getVisibleRows().rows.map((r) => r.id)) === JSON.stringify([4, 2, 5, 3, 1]),
'多列排序按规则顺序生效(city 升序,同城内 age 升序:Beijing 4→2、Shanghai 5→3)',
);
const sizing = createTable({
data: users,
columns: [{ key: 'name', header: 'Name' }],
rowKey: 'id',
plugins: [paginationPlugin()],
initialState: { pagination: { page: 1, pageSize: 2 } },
});
sizing.dispatch({ type: 'setPage', page: 2 });
sizing.dispatch({ type: 'setPageSize', pageSize: 3 });
assert(sizing.getPageInfo().page === 1 && sizing.getPageInfo().pageSize === 3, '改页大小回到第 1 页');
sizing.dispatch({ type: 'toggleSort', key: 'name' });
sizing.dispatch({ type: 'reset' });
assert(
sizing.getState().sorting.length === 0 &&
sizing.getState().filters.length === 0 &&
sizing.getState().pagination.page === 1 &&
sizing.getState().pagination.pageSize === 20,
'reset 回到默认态(排序/筛选清空,分页回 1/20)',
);
// getAllRows:过筛选 + 排序但跳过分页,供导出用
const all = createTable({
data: users,
columns: [{ key: 'name', header: 'Name' }, { key: 'city', header: 'City' }],
rowKey: 'id',
plugins: [filterPlugin(), sortPlugin(), paginationPlugin()],
initialState: { pagination: { page: 1, pageSize: 2 } },
});
assert(all.getVisibleRows().rows.length === 2, 'getVisibleRows 受分页约束只给当页');
all.dispatch({ type: 'setFilter', key: 'city', value: 'Beijing' });
assert(all.getAllRows().rows.length === 2 && all.getVisibleRows().rows.length === 2, 'getAllRows 跳过分页但与筛选一致(Beijing 2 行)');
all.dispatch({ type: 'toggleSort', key: 'age' });
assert(
JSON.stringify(all.getAllRows().rows.map((r) => r.id)) === JSON.stringify([4, 2]),
'getAllRows 仍按排序规则排(age 升序 4→2)',
);
assert(all.getPageInfo().totalPages === 1, 'getAllRows 不影响分页快照(2 行 / 每页 2 = 1 页)');
// setRows:整体换数据源,选择状态按 rowKey 剔除失效行,页码越界由插件收敛
const swapped = createTable({
data: users,
columns: [{ key: 'name', header: 'Name' }],
rowKey: 'id',
plugins: [selectionPlugin(), paginationPlugin()],
initialState: { pagination: { page: 1, pageSize: 2 } },
});
swapped.dispatch({ type: 'toggleSelect', key: 1 }); // 选中两行,后面验证失效行被剔除
swapped.dispatch({ type: 'toggleSelect', key: 4 });
assert(swapped.isRowSelected(1) && swapped.isRowSelected(4), 'setRows 前:两行已选');
swapped.setRows(users.filter((u) => u.id !== 4));
assert(swapped.getVisibleRows().total === 4, 'setRows 后:total 跟随新数据(5 行 → 4 行)');
assert(swapped.isRowSelected(1) && !swapped.isRowSelected(4), 'setRows 后:失效行键从选择里剔除,有效行保留');
assert(swapped.getState().pagination.page === 1, 'setRows 不动页码(用户翻页意图不被打断)');
const sameRows = users.filter((u) => u.id !== 4);
let notified = 0;
swapped.subscribe(() => (notified += 1));
swapped.setRows(sameRows);
assert(notified === 1 && swapped.getVisibleRows().total === 4, 'setRows 换引用:通知一次并重算 total');
console.log('SMOKE OK');