import { expect } from 'chai' import fs from 'node:fs/promises' import { execSync } from 'node:child_process' import { useDocument } from '@terrace-lang/js' import { createFileReader } from '@terrace-lang/js/readers/node-readline' export async function loadTestMap(path) { const reader = createFileReader(path) const doc = useDocument(reader) const descriptions = {} let currentSuite = null let currentTest = null for await (const node of doc) { if (node.head === '#schema') { // Skip schema declarations continue } if (node.head === 'describe') { currentSuite = node.tail descriptions[currentSuite] = [] continue } if (node.head === 'it' && currentSuite) { currentTest = { it: node.tail, packages: [], key: '', input: '', output: '' } descriptions[currentSuite].push(currentTest) // Collect test properties let collectingInput = false let collectingOutput = false let inputLevel = 0 for await (const child of node.children()) { if (child.head === 'key') { currentTest.key = child.tail } else if (child.head === 'packages') { currentTest.packages = child.tail.split(' ') } else if (child.head === 'input') { collectingInput = true collectingOutput = false inputLevel = child.level // If input has content on the same line if (child.tail) { currentTest.input = child.tail } } else if (child.head === 'output') { collectingInput = false collectingOutput = true // If output has content on the same line if (child.tail) { currentTest.output = child.tail } } else if (collectingInput) { // Collect input lines currentTest.input += (currentTest.input ? '\n' : '') + child.raw(inputLevel + 1) } else if (collectingOutput) { // Collect output lines currentTest.output += (currentTest.output ? '\n' : '') + child.content } } // Process escape sequences if (typeof currentTest.input === 'string') { currentTest.input = currentTest.input .replaceAll('\\n', '\n') .replaceAll('\\t', '\t') .replaceAll('\\s', ' ') } if (typeof currentTest.output === 'string') { currentTest.output = currentTest.output.trimEnd() } continue } } return descriptions } export function defineTests(testMap) { for (const [name, tests] of Object.entries(testMap)) { describe(name, () => { for (const test of tests) { test.packages.forEach(pkg => { it(`${pkg}: ${test.it}`, async () => { expect(await callTest(pkg, test.key, test.input)).to.equal(test.output) }) }) } }) } } function callTest(pkg, name, input) { return new Promise((resolve, reject) => { const stdout = execSync(`npm run --silent test ${name}`, { cwd: `../packages/${pkg}`, input }) // TODO: Find a way to clean all this trimming up. Most caused by VSCode happily trimming empty spaces off the ends of lines. resolve(stdout.toString().trim()) }) }