Files
client-web/src/components/common/DataTable.test.mjs
T

79 lines
3.2 KiB
JavaScript
Raw Normal View History

2026-09-19 17:02:11 +08:00
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { parse, compileScript } from 'vue/compiler-sfc'
import ts from 'typescript'
import * as vue from 'vue'
2026-09-19 17:02:11 +08:00
const source = readFileSync(new URL('./DataTable.vue', import.meta.url), 'utf8')
function paginationHarness(t) {
const descriptor = parse(source).descriptor
const compiled = compileScript(descriptor, { id: 'data-table-pagination-test' })
const code = ts.transpileModule(compiled.content, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText
const exports = {}
const deps = { vue, '@lucide/vue': { ChevronLeft: {}, ChevronRight: {} }, './tableActions': {} }
new Function('require', 'exports', code)(id => { assert.ok(id in deps, id); return deps[id] }, exports)
const component = exports.default
component.render = () => null
const state = vue.reactive({ page: 5, total: 5542, loading: false, error: false })
const emitted = []
const renderer = vue.createRenderer({
createElement: tag => ({ tag }), createText: text => ({ text }), createComment: text => ({ text }),
insert() {}, remove() {}, setElementText() {}, setText() {}, patchProp() {},
parentNode: () => null, nextSibling: () => null,
})
const app = renderer.createApp({ render: () => vue.h(component, {
data: [], columns: [], page: state.page, pageSize: 50, total: state.total, loading: state.loading,
'onUpdate:page': value => { emitted.push(value); state.page = value },
}, state.error ? { error: () => vue.h('div', 'Request failed') } : {}) })
app.mount({})
t.after(() => app.unmount())
return { state, emitted }
}
test('server-page loading must not reset the requested page when total temporarily clears', async t => {
const { state, emitted } = paginationHarness(t)
state.loading = true; state.total = 0
await vue.nextTick()
assert.equal(state.page, 5)
assert.deepEqual(emitted, [])
state.total = 5542; state.loading = false
await vue.nextTick()
assert.equal(state.page, 5)
assert.deepEqual(emitted, [])
})
test('failed server page retains its page for explicit retry and emits no automatic fallback request', async t => {
const { state, emitted } = paginationHarness(t)
state.loading = true; state.total = 0
await vue.nextTick()
state.error = true; state.loading = false
await vue.nextTick()
assert.equal(state.page, 5)
assert.deepEqual(emitted, [])
state.error = false; state.loading = true
await vue.nextTick()
state.total = 5542; state.loading = false
await vue.nextTick()
assert.equal(state.page, 5)
assert.deepEqual(emitted, [])
})
test('settled successful totals still clamp out-of-range and empty pages', async t => {
const { state, emitted } = paginationHarness(t)
state.total = 120
await vue.nextTick()
assert.equal(state.page, 3)
state.total = 0
await vue.nextTick()
assert.equal(state.page, 1)
assert.deepEqual(emitted, [3, 1])
})
2026-09-19 17:02:11 +08:00
test('keeps action-only table toolbars on the left', () => {
assert.match(source, /'data-table__toolbar--actions-only': !\$slots\.toolbar/)
assert.match(source, /v-if="\$slots\.toolbar" class="data-table__toolbar-heading"/)
assert.match(source, /\.data-table__toolbar--actions-only \{ justify-content: flex-start; \}/)
})