diff --git a/src/parse.test.ts b/src/parse.test.ts index ad5ecc4..99f2bc3 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -127,6 +127,38 @@ describe("parse", () => { expect(result.commands[0]!.expected).toEqual(["hi"]) }) + test("$# comment line is skipped", () => { + const result = parse("test.shout", "$# start the server\n$ echo hi\nhi\n") + expect(result.commands).toHaveLength(1) + expect(result.commands[0]!.command).toBe("echo hi") + }) + + test("$# comment between commands", () => { + const result = parse("test.shout", "$ echo one\none\n$# now do two\n$ echo two\ntwo\n") + expect(result.commands).toHaveLength(2) + expect(result.commands[0]!.expected).toEqual(["one"]) + expect(result.commands[1]!.expected).toEqual(["two"]) + }) + + test("$# comment with space after hash", () => { + const result = parse("test.shout", "$ # server setup\n$ echo hi\nhi\n") + expect(result.commands).toHaveLength(1) + expect(result.commands[0]!.command).toBe("echo hi") + }) + + test("$# comment as last line", () => { + const result = parse("test.shout", "$ echo hi\nhi\n$# done\n") + expect(result.commands).toHaveLength(1) + expect(result.commands[0]!.expected).toEqual(["hi"]) + }) + + test("output after $# comment is ignored", () => { + const result = parse("test.shout", "$ echo hi\nhi\n$# comment\nstray line\n$ echo bye\nbye\n") + expect(result.commands).toHaveLength(2) + expect(result.commands[0]!.expected).toEqual(["hi"]) + expect(result.commands[1]!.expected).toEqual(["bye"]) + }) + test("no directives returns empty array", () => { const result = parse("test.shout", "$ echo hi\nhi\n") expect(result.directives).toEqual([]) diff --git a/src/parse.ts b/src/parse.ts index 9f271b7..8cd7d1e 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -32,6 +32,11 @@ function stripComment(line: string): string { return line } +/** A line like "$# ..." or "$ # ..." — a comment, not a real command */ +export function isCommentLine(line: string): boolean { + return line.startsWith("$#") || (line.startsWith("$ ") && stripComment(line.slice(2)) === "") +} + function parseExitCode(lines: string[]): { lines: string[] exitCode: number | "*" | null @@ -134,7 +139,18 @@ export function parse(path: string, content: string): ShoutFile { continue } - if (line.startsWith("\\$ ") && current) { + if (isCommentLine(line)) { + // Comment line like "$# ..." or "$ # ..." — finalize current and skip + seenCommand = true + if (current) { + const trimmed = trimTrailingEmpty(current.expected) + const { lines: expectedLines, exitCode } = parseExitCode(trimmed) + current.expected = trimTrailingEmpty(expectedLines) + current.exitCode = exitCode + commands.push(current) + } + current = null + } else if (line.startsWith("\\$ ") && current) { // Escaped dollar-space: literal expected output starting with "$ " current.expected.push(line.slice(1)) } else if (line.startsWith("$ ")) { diff --git a/src/update.ts b/src/update.ts index faeaa93..3546b8a 100644 --- a/src/update.ts +++ b/src/update.ts @@ -1,6 +1,7 @@ import type { CommandResult } from "./run.ts" import type { ShoutFile } from "./parse.ts" import { matchOutput, matchLine } from "./match.ts" +import { isCommentLine } from "./parse.ts" export function rewriteFile( file: ShoutFile, @@ -15,7 +16,10 @@ export function rewriteFile( for (let i = 0; i < lines.length; i++) { const line = lines[i]! - if (line.startsWith("$ ") && !line.startsWith("\\$ ")) { + if (isCommentLine(line)) { + // Preserve comment lines as-is + output.push(line) + } else if (line.startsWith("$ ") && !line.startsWith("\\$ ")) { // Emit the command line as-is output.push(line) @@ -28,7 +32,7 @@ export function rewriteFile( // Skip past old expected output lines in the original let j = i + 1 - while (j < lines.length && !(lines[j]!.startsWith("$ ") && !lines[j]!.startsWith("\\$ "))) { + while (j < lines.length && !isCommentLine(lines[j]!) && !(lines[j]!.startsWith("$ ") && !lines[j]!.startsWith("\\$ "))) { j++ } // Collect old expected lines (before trimming trailing blanks for separator)