pending.nvim/spec/parse_spec.lua
2026-02-24 22:32:14 -05:00

184 lines
5.9 KiB
Lua

require('spec.helpers')
local config = require('pending.config')
describe('parse', function()
before_each(function()
vim.g.pending = nil
config.reset()
end)
after_each(function()
vim.g.pending = nil
config.reset()
end)
local parse = require('pending.parse')
describe('body', function()
it('returns plain description when no metadata', function()
local desc, meta = parse.body('Buy groceries')
assert.are.equal('Buy groceries', desc)
assert.are.same({}, meta)
end)
it('extracts due date', function()
local desc, meta = parse.body('Buy groceries due:2026-03-15')
assert.are.equal('Buy groceries', desc)
assert.are.equal('2026-03-15', meta.due)
end)
it('extracts category', function()
local desc, meta = parse.body('Buy groceries cat:Errands')
assert.are.equal('Buy groceries', desc)
assert.are.equal('Errands', meta.cat)
end)
it('extracts both due and cat', function()
local desc, meta = parse.body('Buy milk due:2026-03-15 cat:Errands')
assert.are.equal('Buy milk', desc)
assert.are.equal('2026-03-15', meta.due)
assert.are.equal('Errands', meta.cat)
end)
it('extracts metadata in any order', function()
local desc, meta = parse.body('Buy milk cat:Errands due:2026-03-15')
assert.are.equal('Buy milk', desc)
assert.are.equal('2026-03-15', meta.due)
assert.are.equal('Errands', meta.cat)
end)
it('stops at duplicate key', function()
local desc, meta = parse.body('Buy milk due:2026-03-15 due:2026-04-01')
assert.are.equal('Buy milk due:2026-03-15', desc)
assert.are.equal('2026-04-01', meta.due)
end)
it('stops at non-meta token', function()
local desc, meta = parse.body('Buy milk for breakfast due:2026-03-15')
assert.are.equal('Buy milk for breakfast', desc)
assert.are.equal('2026-03-15', meta.due)
end)
it('rejects invalid dates', function()
local desc, meta = parse.body('Buy milk due:2026-13-15')
assert.are.equal('Buy milk due:2026-13-15', desc)
assert.is_nil(meta.due)
end)
it('preserves colons in description', function()
local desc, meta = parse.body('Meeting at 3:00pm')
assert.are.equal('Meeting at 3:00pm', desc)
assert.are.same({}, meta)
end)
it('uses configurable date syntax', function()
vim.g.pending = { date_syntax = 'by' }
config.reset()
local desc, meta = parse.body('Buy milk by:2026-03-15')
assert.are.equal('Buy milk', desc)
assert.are.equal('2026-03-15', meta.due)
end)
it('ignores old syntax when date_syntax is changed', function()
vim.g.pending = { date_syntax = 'by' }
config.reset()
local desc, meta = parse.body('Buy milk due:2026-03-15')
assert.are.equal('Buy milk due:2026-03-15', desc)
assert.is_nil(meta.due)
end)
it('resolves due:today to today date', function()
local desc, meta = parse.body('Buy milk due:today')
assert.are.equal('Buy milk', desc)
assert.are.equal(os.date('%Y-%m-%d'), meta.due)
end)
it('resolves due:+2d to today plus 2 days', function()
local today = os.date('*t') --[[@as osdate]]
local expected = os.date(
'%Y-%m-%d',
os.time({ year = today.year, month = today.month, day = today.day + 2 })
)
local desc, meta = parse.body('Task due:+2d')
assert.are.equal('Task', desc)
assert.are.equal(expected, meta.due)
end)
it('leaves unresolvable due token in description', function()
local desc, meta = parse.body('Task due:garbage')
assert.is_nil(meta.due)
assert.truthy(desc:find('due:garbage', 1, true))
end)
end)
describe('parse.resolve_date', function()
it("returns today's date for 'today'", function()
local result = parse.resolve_date('today')
assert.are.equal(os.date('%Y-%m-%d'), result)
end)
it("returns tomorrow's date for 'tomorrow'", function()
local expected = os.date('%Y-%m-%d', os.time() + 86400)
local result = parse.resolve_date('tomorrow')
assert.are.equal(expected, result)
end)
it("returns today + 3 days for '+3d'", function()
local today = os.date('*t') --[[@as osdate]]
local expected = os.date(
'%Y-%m-%d',
os.time({ year = today.year, month = today.month, day = today.day + 3 })
)
local result = parse.resolve_date('+3d')
assert.are.equal(expected, result)
end)
it("returns today for '+0d'", function()
local result = parse.resolve_date('+0d')
assert.are.equal(os.date('%Y-%m-%d'), result)
end)
it("returns a future Monday (or today) for 'mon'", function()
local result = parse.resolve_date('mon')
assert.is_not_nil(result)
assert.truthy(result:match('^%d%d%d%d%-%d%d%-%d%d$'))
end)
it('returns nil for garbage input', function()
local result = parse.resolve_date('notadate')
assert.is_nil(result)
end)
it('returns nil for empty string', function()
local result = parse.resolve_date('')
assert.is_nil(result)
end)
end)
describe('command_add', function()
it('parses simple text', function()
local desc, meta = parse.command_add('Buy milk')
assert.are.equal('Buy milk', desc)
assert.are.same({}, meta)
end)
it('detects category prefix', function()
local desc, meta = parse.command_add('School: Do homework')
assert.are.equal('Do homework', desc)
assert.are.equal('School', meta.cat)
end)
it('ignores lowercase prefix', function()
local desc, _ = parse.command_add('hello: world')
assert.are.equal('hello: world', desc)
end)
it('combines category prefix with inline metadata', function()
local desc, meta = parse.command_add('School: Do homework due:2026-03-15')
assert.are.equal('Do homework', desc)
assert.are.equal('School', meta.cat)
assert.are.equal('2026-03-15', meta.due)
end)
end)
end)