/ scripts / run-js-doc-tests.ts
run-js-doc-tests.ts
  1  const LORO_VERSION = "1.3.5";
  2  
  3  export interface CodeBlock {
  4      filename: string;
  5      filePath: string;
  6      lineNumber: number;
  7      lang: string;
  8      content: string;
  9  }
 10  
 11  export function extractCodeBlocks(
 12      fileContent: string,
 13      codeBlocks: CodeBlock[],
 14      name: string,
 15      path: string,
 16  ) {
 17      // Regular expression to detect TypeScript code blocks
 18      const codeBlockRegex = /```(typescript|ts|js|javascript)\n([\s\S]*?)```/g;
 19      let match;
 20      while ((match = codeBlockRegex.exec(fileContent)) !== null) {
 21          const startLine =
 22              fileContent.substring(0, match.index).split("\n").length;
 23          let content = match[2];
 24          content = content.replace(/^\s*\*/g, "");
 25          content = content.replace(/\n\s*\*/g, "\n");
 26          content = content.replace(/^\s*\/\/\//g, "");
 27          content = content.replace(/\n\s*\/\/\//g, "\n");
 28          content = replaceImportVersion(content, LORO_VERSION);
 29          if (!content.includes("loro-crdt")) {
 30              content = IMPORTS + content;
 31          }
 32          codeBlocks.push({
 33              filename: name,
 34              filePath: path,
 35              lineNumber: startLine,
 36              content,
 37              lang: match[1],
 38          });
 39      }
 40  }
 41  
 42  function replaceImportVersion(input: string, targetVersion: string): string {
 43      const regex = /from "loro-crdt"/g;
 44      const replacement = `from "npm:loro-crdt@${targetVersion}"`;
 45      return input.replace(regex, replacement);
 46  }
 47  
 48  const IMPORTS =
 49      `import { Loro, LoroDoc, LoroMap, LoroText, LoroList, Delta, UndoManager, getType, isContainer } from "npm:loro-crdt@${LORO_VERSION}";
 50  import { expect } from "npm:expect@29.7.0";\n
 51  `;
 52  
 53  Deno.test("extract doc tests", async () => {
 54      const filePath = "./doc-tests-tests/example.txt";
 55      const fileContent = await Deno.readTextFile(filePath);
 56      const codeBlocks: CodeBlock[] = [];
 57      extractCodeBlocks(fileContent, codeBlocks, "example.txt", filePath);
 58      for (const block of codeBlocks) {
 59          console.log(block.content);
 60          console.log("==============================");
 61      }
 62      await runCodeBlocks(codeBlocks);
 63  });
 64  
 65  export async function runDocTests(paths: string[]) {
 66      const codeBlocks: CodeBlock[] = [];
 67      for (const path of paths) {
 68          const fileContent = await Deno.readTextFile(path);
 69          extractCodeBlocks(fileContent, codeBlocks, path, path);
 70      }
 71  
 72      await runCodeBlocks(codeBlocks);
 73  }
 74  
 75  async function runCodeBlocks(codeBlocks: CodeBlock[]) {
 76      let testCases = 0;
 77      let passed = 0;
 78      let failed = 0;
 79      for (const block of codeBlocks) {
 80          try {
 81              const command = new Deno.Command("deno", {
 82                  args: ["eval", "--ext=ts", block.content],
 83                  stdout: "null",
 84                  stderr: "inherit",
 85              });
 86              const process = command.spawn();
 87              const status = await process.status;
 88              testCases += 1;
 89              if (status.success) {
 90                  passed += 1;
 91              } else {
 92                  console.log("----------------");
 93                  console.log(block.content);
 94                  console.log("-----------------");
 95                  console.error(
 96                      `\x1b[31;1mError in \x1b[4m${block.filePath}:${block.lineNumber}\x1b[0m\n\n\n\n\n`,
 97                  );
 98                  failed += 1;
 99              }
100          } catch (error) {
101              console.error("Error:", error);
102          }
103          await Deno.stdout.write(
104              new TextEncoder().encode(
105                  `\r🧪 ${testCases} tests, ✅ ${passed} passed,${failed > 0 ? " ❌" : ""
106                  } ${failed} failed`,
107              ),
108          );
109      }
110  }
111  
112  if (Deno.args.length > 0) {
113      await runDocTests(Deno.args);
114  } else {
115      console.log("No paths provided. Please provide paths as arguments.");
116  }