trans2 / ui /tests /lib /ops.test.ts
Mayo
test: Rust integration tests + Vitest UI tests
5242e6a unverified
import { describe, expect, it } from 'vitest'
import type { Node, Page } from '@/lib/api/schemas'
import { ops } from '@/lib/ops'
// The op constructors are pure — they shape JSON for the wire format. These
// tests pin the exact shape so accidental renames break loudly.
const samplePage = (): Page =>
({
id: 'p-1',
name: 'Page 1',
width: 100,
height: 200,
nodes: {},
}) as unknown as Page
const sampleNode = (): Node =>
({
id: 'n-1',
transform: { x: 0, y: 0, width: 10, height: 10, rotationDeg: 0 },
visible: true,
kind: { text: {} },
}) as unknown as Node
describe('ops constructors', () => {
it('updateProjectMeta wraps patch', () => {
expect(ops.updateProjectMeta({ name: 'x' })).toEqual({
updateProjectMeta: { patch: { name: 'x' } },
})
})
it('addPage carries at + page', () => {
const page = samplePage()
expect(ops.addPage(3, page)).toEqual({ addPage: { at: 3, page } })
})
it('removePage renames fields for wire format', () => {
const page = samplePage()
expect(ops.removePage('id-1', page, 2)).toEqual({
removePage: { id: 'id-1', prev_page: page, prev_index: 2 },
})
})
it('updatePage wraps patch', () => {
expect(ops.updatePage('p-1', { name: 'renamed' })).toEqual({
updatePage: { id: 'p-1', patch: { name: 'renamed' } },
})
})
it('reorderPages preserves order and prev_order casing', () => {
expect(ops.reorderPages(['a', 'b'], ['b', 'a'])).toEqual({
reorderPages: { order: ['a', 'b'], prev_order: ['b', 'a'] },
})
})
it('addNode carries page + at + node', () => {
const node = sampleNode()
expect(ops.addNode('p-1', 0, node)).toEqual({
addNode: { page: 'p-1', at: 0, node },
})
})
it('removeNode carries prev_node and prev_index', () => {
const node = sampleNode()
expect(ops.removeNode('p-1', 'n-1', node, 4)).toEqual({
removeNode: { page: 'p-1', id: 'n-1', prev_node: node, prev_index: 4 },
})
})
it('updateNode wraps patch', () => {
expect(ops.updateNode('p-1', 'n-1', { visible: false })).toEqual({
updateNode: { page: 'p-1', id: 'n-1', patch: { visible: false } },
})
})
it('reorderNodes preserves order + prev_order', () => {
expect(ops.reorderNodes('p-1', ['a'], ['a'])).toEqual({
reorderNodes: { page: 'p-1', order: ['a'], prev_order: ['a'] },
})
})
it('batch wraps label + ops', () => {
const inner = ops.updatePage('p-1', {})
expect(ops.batch('refactor', [inner])).toEqual({
batch: { label: 'refactor', ops: [inner] },
})
})
it('moveNode is shorthand for updateNode with transform', () => {
const t = { x: 1, y: 2, width: 3, height: 4, rotationDeg: 0 }
expect(ops.moveNode('p', 'n', t)).toEqual({
updateNode: { page: 'p', id: 'n', patch: { transform: t } },
})
})
it('setNodeVisible toggles visible', () => {
expect(ops.setNodeVisible('p', 'n', true)).toEqual({
updateNode: { page: 'p', id: 'n', patch: { visible: true } },
})
})
it('updateText wraps text inside data', () => {
expect(ops.updateText('p', 'n', { raw: 'hi' } as never)).toEqual({
updateNode: { page: 'p', id: 'n', patch: { data: { text: { raw: 'hi' } } } },
})
})
it('updateImage wraps image inside data', () => {
expect(ops.updateImage('p', 'n', { opacity: 1 } as never)).toEqual({
updateNode: { page: 'p', id: 'n', patch: { data: { image: { opacity: 1 } } } },
})
})
it('updateMask wraps mask inside data', () => {
expect(ops.updateMask('p', 'n', { opacity: 0.5 } as never)).toEqual({
updateNode: { page: 'p', id: 'n', patch: { data: { mask: { opacity: 0.5 } } } },
})
})
})