File size: 7,713 Bytes
e5167b8
 
 
86725f7
e5167b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86725f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e5167b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Unit tests for the structure YAML grammar and the pure text mutations
// behind sidebar drag-and-drop and groups. No server needed.
import assert from 'node:assert'
import { parseStructure, moveNodeInYaml, moveNodesInYaml, addGroupToYaml, renameGroupInYaml, dissolveGroupInYaml, validGroupLabel } from '../server/pages.js'

const YAML = [
  '- home',
  '- "Getting started":',
  '  - intro',
  '  - setup',
  '- design',
  '  - communication',
  '- references',
].join('\n')

// --- parsing ---
{
  const { tree, problems } = parseStructure(YAML)
  assert.deepStrictEqual(problems, [])
  assert.strictEqual(tree.length, 4)
  assert.strictEqual(tree[0].slug, 'home')
  assert.strictEqual(tree[1].group, 'Getting started')
  assert.deepStrictEqual(tree[1].children.map(c => c.slug), ['intro', 'setup'])
  assert.strictEqual(tree[2].children[0].slug, 'communication')
}
{
  // problem lines are reported, groups can nest, comments are skipped
  const { tree, problems } = parseStructure('# comment\n- "A":\n  - "B":\n    - x\n- ???')
  assert.strictEqual(problems.length, 1)
  assert.strictEqual(tree[0].group, 'A')
  assert.strictEqual(tree[0].children[0].group, 'B')
  assert.strictEqual(tree[0].children[0].children[0].slug, 'x')
}

// --- move: reorder at root ---
{
  const { raw } = moveNodeInYaml(YAML, 'references', null, 0)
  assert.strictEqual(raw.split('\n')[0], '- references')
  assert.strictEqual(parseStructure(raw).tree[0].slug, 'references')
}

// --- move: nest a page (subtree travels, reindents) ---
{
  const { raw } = moveNodeInYaml(YAML, 'design', ['"Getting started"'], 0)
  const t = parseStructure(raw).tree
  const g = t.find(n => n.group === 'Getting started')
  assert.deepStrictEqual(g.children.map(c => c.slug), ['design', 'intro', 'setup'])
  assert.strictEqual(g.children[0].children[0].slug, 'communication')
  assert.ok(raw.includes('    - communication'))
}

// --- move: out of a group to root, append ---
{
  const { raw } = moveNodeInYaml(YAML, 'intro', null, 99)
  const t = parseStructure(raw).tree
  assert.strictEqual(t[t.length - 1].slug, 'intro')
  const g = t.find(n => n.group === 'Getting started')
  assert.deepStrictEqual(g.children.map(c => c.slug), ['setup'])
}

// --- move: index counts after removal (down within same parent) ---
{
  const { raw } = moveNodeInYaml(YAML, 'home', null, 2)
  assert.deepStrictEqual(
    parseStructure(raw).tree.map(n => n.slug || n.group),
    ['Getting started', 'design', 'home', 'references']
  )
}

// --- move: under a page (pages nest pages) ---
{
  const { raw } = moveNodeInYaml(YAML, 'references', 'design', 99)
  const d = parseStructure(raw).tree.find(n => n.slug === 'design')
  assert.deepStrictEqual(d.children.map(c => c.slug), ['communication', 'references'])
}

// --- move: unfiled page gets a line ---
{
  const { raw } = moveNodeInYaml(YAML, 'notes', ['"Getting started"'], 1)
  const g = parseStructure(raw).tree.find(n => n.group === 'Getting started')
  assert.deepStrictEqual(g.children.map(c => c.slug), ['intro', 'notes', 'setup'])
}

// --- move: comments inside a block travel with it ---
{
  const withComment = YAML.replace('  - communication', '  # keep\n  - communication')
  const { raw } = moveNodeInYaml(withComment, 'design', null, 0)
  const lines = raw.split('\n')
  assert.ok(lines.indexOf('  # keep') === lines.indexOf('- design') + 1)
}

// --- move: rejections ---
assert.ok(moveNodeInYaml(YAML, 'design', 'communication', 0).error, 'nest into own subtree')
assert.ok(moveNodeInYaml(YAML, '_structure', null, 0).error, 'move _structure')
assert.ok(moveNodeInYaml(YAML, 'home', ['"Nope"'], 0).error, 'unknown parent')
assert.ok(moveNodeInYaml(YAML, ['"Nope"'], null, 0).error, 'unknown group node')
assert.ok(moveNodeInYaml(YAML, 'bad slug!', null, 0).error, 'bad slug')

// --- groups: add / rename / dissolve ---
{
  const { raw } = addGroupToYaml(YAML, 'References & links', null, 1)
  const t = parseStructure(raw).tree
  assert.strictEqual(t[1].group, 'References & links')
  assert.ok(addGroupToYaml(raw, 'References & links', null, 0).error, 'duplicate sibling label')
}
{
  const { raw } = addGroupToYaml(YAML, 'Sub', ['"Getting started"'], 99)
  const g = parseStructure(raw).tree.find(n => n.group === 'Getting started')
  assert.strictEqual(g.children[2].group, 'Sub')
}
{
  const { raw } = renameGroupInYaml(YAML, ['"Getting started"'], 'Start here')
  const t = parseStructure(raw).tree
  assert.strictEqual(t[1].group, 'Start here')
  assert.deepStrictEqual(t[1].children.map(c => c.slug), ['intro', 'setup'])
  assert.ok(renameGroupInYaml(YAML, ['"Getting started"'], 'x'.repeat(61)).error, 'label too long')
  assert.ok(renameGroupInYaml(YAML, ['"Nope"'], 'ok').error, 'unknown group')
}
{
  const { raw } = dissolveGroupInYaml(YAML, ['"Getting started"'])
  const t = parseStructure(raw).tree
  assert.deepStrictEqual(t.map(n => n.slug), ['home', 'intro', 'setup', 'design', 'references'])
  assert.ok(dissolveGroupInYaml(YAML, ['home']).error, 'dissolve a page')
}

// --- multi-node move (sidebar multi-select) ---
{
  // two pages from different parents land as consecutive siblings, doc order
  const { raw } = moveNodesInYaml(YAML, ['setup', 'references'], 'design', 0)
  const d = parseStructure(raw).tree.find(n => n.slug === 'design')
  assert.deepStrictEqual(d.children.map(c => c.slug), ['setup', 'references', 'communication'])
}
{
  // a group and its own child selected together: the child travels once
  const { raw } = moveNodesInYaml(YAML, [['"Getting started"'], 'intro'], 'design', 99)
  const t = parseStructure(raw).tree
  assert.deepStrictEqual(t.map(n => n.slug || n.group), ['home', 'design', 'references'])
  const d = t.find(n => n.slug === 'design')
  const g = d.children.find(n => n.group === 'Getting started')
  assert.deepStrictEqual(g.children.map(c => c.slug), ['intro', 'setup'])
  assert.strictEqual(d.children.filter(n => n.slug === 'intro').length, 0, 'intro not duplicated')
}
{
  // index counts after ALL moved nodes leave: home+intro to root index 2
  const { raw } = moveNodesInYaml(YAML, ['home', 'intro'], null, 2)
  assert.deepStrictEqual(
    parseStructure(raw).tree.map(n => n.slug || n.group),
    ['Getting started', 'design', 'home', 'intro', 'references']
  )
}
{
  // mixed tree + unfiled refs; duplicates collapse
  const { raw } = moveNodesInYaml(YAML, ['setup', 'notes', 'setup'], null, 0)
  const t = parseStructure(raw).tree
  assert.deepStrictEqual(t.slice(0, 2).map(n => n.slug), ['setup', 'notes'])
  assert.strictEqual(JSON.stringify(t).split('"setup"').length, 2, 'setup appears once')
}
assert.ok(moveNodesInYaml(YAML, [], null, 0).error, 'empty selection')
assert.ok(moveNodesInYaml(YAML, ['home', 'design'], 'communication', 0).error, 'parent inside a moved subtree')

// --- labels ---
assert.strictEqual(validGroupLabel('  Reading list  '), 'Reading list')
assert.strictEqual(validGroupLabel('a"b'), null)
assert.strictEqual(validGroupLabel(''), null)

// --- a round of moves keeps the yaml parseable with zero problems ---
{
  let raw = YAML
  for (const [node, parent, index] of [
    ['setup', null, 0],
    ['design', ['"Getting started"'], 0],
    ['setup', 'design', 99],
    ['home', 'setup', 0],
  ]) {
    const r = moveNodeInYaml(raw, node, parent, index)
    assert.ok(!r.error, `${node}: ${r.error}`)
    raw = r.raw
    assert.deepStrictEqual(parseStructure(raw).problems, [])
  }
  const g = parseStructure(raw).tree.find(n => n.group === 'Getting started')
  assert.strictEqual(g.children[0].slug, 'design')
  assert.strictEqual(g.children[0].children[1].slug, 'setup')
  assert.strictEqual(g.children[0].children[1].children[0].slug, 'home')
}

console.log('structure tests passed')