OverviewFilesCommitsIssuesDocsReleasesPeers

codetree / test / traverse.test.js

91 lines · javascript ref:
import { test } from 'brittle'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import { walkGraph } from '../lib/traverse.js'

const HERE = dirname(fileURLToPath(import.meta.url))

// Was `resolve(HERE, '../../')` — the live hyper-fleeky tree. A sibling repo's
// version bump turned two assertions here red (invariant I1). fixtures/workspace
// is ours and cannot drift; see its README for what stands in for what.
const FIXTURE = resolve(HERE, 'fixtures/workspace')

test('traverse: a single module', async (t) => {
  const graph = await walkGraph(resolve(FIXTURE, 'widget-store'))

  t.is(graph.modules.size, 1, '1 module found')
  t.ok(graph.modules.has('widget-store'), 'found widget-store')

  const mod = graph.modules.get('widget-store')
  t.is(mod.version, '0.2.0', 'correct version')
  t.ok(mod.entry.endsWith('index.js'), 'entry is index.js')
  t.ok(mod.dependencies['widget-rooms'], 'depends on widget-rooms')
  t.is(mod.testCommand, 'brittle test/*.test.js', 'test command')
  t.is(mod.type, 'module', 'ESM module')

  // Files
  t.ok(mod.files.has('index.js'), 'has index.js')
  t.ok(mod.files.has('lib/widget-store.js'), 'has lib/widget-store.js')
  t.ok(mod.files.has('lib/schema.js'), 'has lib/schema.js')
  t.ok(mod.files.has('lib/handlers/widgets.js'), 'walks nested directories')
  t.ok(mod.files.has('test/store.test.js'), 'includes the test file')

  // File is parsed (source no longer retained — see TreeReducer.onFileDiscovered)
  const indexFile = mod.files.get('index.js')
  t.ok(indexFile.parsed, 'index.js has parsed result')
  t.is(indexFile.parsed.exports.length, 4, 'index.js has its 4 exports')
})

test('traverse: file relationships', async (t) => {
  const graph = await walkGraph(resolve(FIXTURE, 'widget-store'))
  const mod = graph.modules.get('widget-store')

  // The barrel imports from lib/
  const indexFile = mod.files.get('index.js')
  t.alike(indexFile.localImports.map(i => i.specifier).sort(),
    ['./lib/schema.js', './lib/widget-store.js'], 'barrel resolves both lib imports')

  // lib/widget-store.js imports schema.js — a relative edge one level down
  const mainFile = mod.files.get('lib/widget-store.js')
  t.alike(mainFile.localImports.map(i => i.specifier), ['./schema.js'],
    'widget-store.js imports schema.js')

  // 'widget-rooms' is a package, not a local edge, so it is NOT a localImport
  t.absent(mainFile.localImports.find(i => i.specifier === 'widget-rooms'),
    'a bare specifier is not a local import')
})

test('traverse: import maps', async (t) => {
  const graph = await walkGraph(resolve(FIXTURE, 'mapped-io'))

  const mod = graph.modules.get('mapped-io')
  t.ok(mod, 'found mapped-io')
  t.ok(mod.importMaps, 'has import maps')
  t.alike(Object.keys(mod.importMaps).sort(), ['fs', 'os', 'path'], 'all three maps')
  t.is(mod.importMaps.fs.bare, 'bare-fs', 'fs maps to bare-fs under Bare')
  t.is(mod.importMaps.fs.default, 'fs', 'and to fs by default')
})

test('traverse: multi-module scan', async (t) => {
  const graph = await walkGraph(FIXTURE, { maxDepth: 1 })

  // Deterministic now: the old version could only assert "more than five",
  // because it was counting whatever happened to be checked out.
  t.is(graph.modules.size, 4, '4 modules')
  t.alike(Array.from(graph.modules.keys()).sort(),
    ['legacy-sync', 'mapped-io', 'widget-rooms', 'widget-store'], 'the expected set')

  for (const [name, mod] of graph.modules) {
    t.ok(mod.files.size > 0, name + ' has ' + mod.files.size + ' files')
  }
})

test('traverse: a CJS module', async (t) => {
  const graph = await walkGraph(resolve(FIXTURE, 'legacy-sync'))

  const mod = graph.modules.get('legacy-sync')
  t.ok(mod, 'found legacy-sync')
  t.is(mod.type, 'commonjs', 'detected as commonjs (no type field)')
  t.ok(mod.files.has('index.js'), 'has index.js')
})