@@ -34,9 +34,9 @@ async function openChanged(): Promise<HTMLElement> {
|
||||
}
|
||||
|
||||
describe('Editor view modes', () => {
|
||||
it('Updated mode is an editable buffer; Original is read-only', async () => {
|
||||
it('Actual mode is an editable buffer; Original is read-only', async () => {
|
||||
const c = await openChanged()
|
||||
fireEvent.click(find(c, '.seg button', 'Updated')!)
|
||||
fireEvent.click(find(c, '.seg button', 'Actual')!)
|
||||
await waitFor(() => expect(c.querySelector('.ce-ta')).toBeTruthy())
|
||||
|
||||
fireEvent.click(find(c, '.seg button', 'Original')!)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { simpleGit } from 'simple-git'
|
||||
import { buildTreeFromPaths, createProjectFile, readAll, readProjectFile, readTree, writeProjectFile } from '../src/main/fs-service'
|
||||
import { buildTreeFromPaths, createProjectFile, readAll, readDirChildren, readProjectFile, readTree, writeProjectFile } from '../src/main/fs-service'
|
||||
|
||||
let dir = ''
|
||||
afterEach(async () => { if (dir) await rm(dir, { recursive: true, force: true }) })
|
||||
@@ -33,6 +33,51 @@ describe('readTree', () => {
|
||||
expect(top.indexOf('src')).toBeLessThan(top.indexOf('README.md'))
|
||||
expect(top.indexOf('src')).toBeLessThan(top.indexOf('logo.bin'))
|
||||
})
|
||||
|
||||
it('shows an empty folder (no files yet) that rg --files can not emit', async () => {
|
||||
dir = await fixture()
|
||||
await mkdir(join(dir, 'empty'), { recursive: true })
|
||||
await mkdir(join(dir, 'src', 'nested', 'deep'), { recursive: true }) // file-empty nested chain
|
||||
const tree = await readTree(dir)
|
||||
const top = (tree.children || []).map((c) => c.name)
|
||||
expect(top).toContain('empty')
|
||||
const empty = (tree.children || []).find((c) => c.name === 'empty')!
|
||||
expect(empty.type).toBe('dir')
|
||||
// the nested empty chain shows under the (file-bearing) src folder
|
||||
const src = (tree.children || []).find((c) => c.name === 'src')!
|
||||
const nested = (src.children || []).find((c) => c.name === 'nested')!
|
||||
expect(nested.type).toBe('dir')
|
||||
expect((nested.children || []).map((c) => c.name)).toEqual(['deep'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('readDirChildren', () => {
|
||||
it('returns a single folder\'s children (root and subdir), ignoring node_modules', async () => {
|
||||
dir = await fixture()
|
||||
const top = (await readDirChildren(dir, '')).map((c) => c.name)
|
||||
expect(top).toContain('src')
|
||||
expect(top).toContain('README.md')
|
||||
expect(top).not.toContain('node_modules')
|
||||
const src = await readDirChildren(dir, 'src')
|
||||
expect(src.map((c) => c.name)).toEqual(['a.ts'])
|
||||
expect(src[0].path).toBe('src/a.ts')
|
||||
})
|
||||
|
||||
it('shows an empty subfolder created since the initial tree read', async () => {
|
||||
dir = await fixture()
|
||||
await mkdir(join(dir, 'src', 'fresh'), { recursive: true })
|
||||
const src = await readDirChildren(dir, 'src')
|
||||
const fresh = src.find((c) => c.name === 'fresh')
|
||||
expect(fresh?.type).toBe('dir')
|
||||
expect(fresh?.path).toBe('src/fresh')
|
||||
})
|
||||
|
||||
it('reflects files added/removed since the initial tree read', async () => {
|
||||
dir = await fixture()
|
||||
await writeFile(join(dir, 'src', 'b.ts'), 'export const b = 2\n')
|
||||
await rm(join(dir, 'src', 'a.ts'))
|
||||
expect((await readDirChildren(dir, 'src')).map((c) => c.name)).toEqual(['b.ts'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('readAll', () => {
|
||||
@@ -67,6 +112,15 @@ describe('buildTreeFromPaths', () => {
|
||||
expect((src.children || []).map((c) => c.name)).toEqual(['util', 'a.ts', 'b.ts'])
|
||||
expect((src.children || []).find((c) => c.name === 'util')!.path).toBe('src/util')
|
||||
})
|
||||
|
||||
it('forces dirPaths in as empty folders, deduped against file-derived dirs', () => {
|
||||
const t = buildTreeFromPaths('proj', ['src/a.ts'], ['empty', 'src/sub', 'src'])
|
||||
const top = (t.children || []).map((c) => c.name)
|
||||
expect(top).toEqual(['empty', 'src']) // dirs alphabetical, both present once
|
||||
const src = (t.children || []).find((c) => c.name === 'src')!
|
||||
expect((src.children || []).map((c) => c.name)).toEqual(['sub', 'a.ts'])
|
||||
expect((src.children || []).find((c) => c.name === 'sub')!.type).toBe('dir')
|
||||
})
|
||||
})
|
||||
|
||||
describe('read/write round-trip', () => {
|
||||
|
||||
44
test/markdown.test.ts
Normal file
44
test/markdown.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { renderMarkdown } from '../src/renderer/src/markdown'
|
||||
|
||||
describe('renderMarkdown', () => {
|
||||
it('renders headings, bold, italic and links', () => {
|
||||
const html = renderMarkdown('# Title\n\nSome **bold** and *italic* and [a](https://x.com).')
|
||||
expect(html).toContain('<h1>Title</h1>')
|
||||
expect(html).toContain('<strong>bold</strong>')
|
||||
expect(html).toContain('<em>italic</em>')
|
||||
expect(html).toContain('<a href="https://x.com" target="_blank" rel="noreferrer">a</a>')
|
||||
})
|
||||
|
||||
it('restores code spans without colliding with surrounding digits', () => {
|
||||
// " 0 " around the text used to clash with the placeholder index — guard it.
|
||||
const html = renderMarkdown('I have 0 cats and `code` and 1 dog.')
|
||||
expect(html).toContain('<code>code</code>')
|
||||
expect(html).toContain('I have 0 cats')
|
||||
expect(html).toContain('1 dog.')
|
||||
expect(html).not.toContain('undefined')
|
||||
})
|
||||
|
||||
it('escapes HTML and never passes through raw tags', () => {
|
||||
const html = renderMarkdown('A <script>alert(1)</script> tag.')
|
||||
expect(html).toContain('<script>')
|
||||
expect(html).not.toContain('<script>')
|
||||
})
|
||||
|
||||
it('drops dangerous link schemes but keeps the text', () => {
|
||||
const html = renderMarkdown('[click](javascript:alert(1))')
|
||||
expect(html).not.toContain('javascript:')
|
||||
expect(html).toContain('click')
|
||||
})
|
||||
|
||||
it('highlights fenced code blocks', () => {
|
||||
const html = renderMarkdown('```js\nconst a = 1\n```')
|
||||
expect(html).toContain('<pre class="md-code">')
|
||||
expect(html).toContain('const')
|
||||
})
|
||||
|
||||
it('renders unordered and ordered lists', () => {
|
||||
expect(renderMarkdown('- a\n- b')).toContain('<ul><li>a</li><li>b</li></ul>')
|
||||
expect(renderMarkdown('1. a\n2. b')).toContain('<ol><li>a</li><li>b</li></ol>')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user