OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / repo-blob-media.test.js

71 lines · javascript ref:
// Tier-1 media handling in the repo file viewer: images, SVG, and CSV/TSV.
// renderRepoBlob is pure SSR — we drive it with a stub sync service so the
// test needs no daemon, swarm, or real repo.

import test from 'brittle'
import { renderRepoBlob, renderCsvTable } from '../lib/web/pages/repo-browser.js'

function stubSvc (overrides = {}) {
  const sync = {
    isBinary: async () => false,
    getBranches: async () => [{ name: 'main', current: true }],
    getTags: async () => [],
    readFile: async () => 'col1,col2\nx,y',
    ...overrides
  }
  return { _sync: sync }
}

const CTX = { prefix: '', user: null }

test('renderCsvTable: header row, escaping, quoted fields', (t) => {
  const csv = 'name,score\n"a,b",10\n"q""x",<script>'
  const html = renderCsvTable(csv, ',')
  t.ok(html.includes('<th>name</th>'), 'first row becomes <th>')
  t.ok(html.includes('<td>a,b</td>'), 'quoted field keeps embedded comma')
  t.ok(html.includes('q&quot;x') || html.includes('q&#34;x'), 'escaped "" quote')
  t.ok(html.includes('&lt;script&gt;'), 'cell HTML is escaped')
  t.absent(html.includes('<script>'), 'no raw script tag leaks through')
})

test('renderCsvTable: TSV delimiter', (t) => {
  const html = renderCsvTable('a\tb\n1\t2', '\t')
  t.ok(html.includes('<th>a</th>') && html.includes('<th>b</th>'), 'tab split')
  t.ok(html.includes('<td>1</td>') && html.includes('<td>2</td>'), 'tab data row')
})

test('renderCsvTable: row cap reports true total', (t) => {
  let csv = 'h\n'
  for (let i = 0; i < 1500; i++) csv += i + '\n'
  const html = renderCsvTable(csv, ',')
  t.ok(html.includes('Showing first 1000 of 1501 rows'), 'caps display, counts all')
})

test('renderRepoBlob: raster image renders as <img> to /raw, no byte read', async (t) => {
  let readCalled = false
  const svc = stubSvc({ readFile: async () => { readCalled = true; return 'x' } })
  const out = await renderRepoBlob(svc, CTX, 'demo', 'data/pixel.png', 'HEAD')
  t.ok(/<img src="[^"]*\/repos\/demo\/raw\/data\/pixel\.png"/.test(out), 'img points at raw route')
  t.absent(readCalled, 'image bytes are not loaded into a string')
  t.ok(out.includes('view raw'), 'offers a raw link')
})

test('renderRepoBlob: svg renders as <img> (not source)', async (t) => {
  const out = await renderRepoBlob(svc(), CTX, 'demo', 'logo.svg', 'HEAD')
  t.ok(out.includes('/repos/demo/raw/logo.svg'), 'svg uses raw route')
  t.ok(out.includes('<img'), 'svg shown as image, not highlighted source')
  function svc () { return stubSvc() }
})

test('renderRepoBlob: csv renders a table', async (t) => {
  const svc = stubSvc({ readFile: async () => 'col1,col2\nx,y' })
  const out = await renderRepoBlob(svc, CTX, 'demo', 'data/t.csv', 'HEAD')
  t.ok(out.includes('csv-table'), 'uses csv table')
  t.ok(out.includes('<th>col1</th>'), 'csv header rendered')
})

test('renderRepoBlob: ref carried into raw url', async (t) => {
  const out = await renderRepoBlob(stubSvc(), CTX, 'demo', 'a.png', 'v1.0')
  t.ok(out.includes('/repos/demo/raw/a.png?ref=v1.0'), 'non-HEAD ref appended')
})