From 147b6ffede73d507ac7177d9e82bd8f246dd2cb0 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 18 Sep 2026 15:01:08 -0400 Subject: [PATCH 1/3] test: add end-to-end tests for recommended config Co-Authored-By: Claude Opus 5 --- tests/autofix.test.ts | 14 +++++++ tests/plugin.test.ts | 90 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/tests/autofix.test.ts b/tests/autofix.test.ts index dca33ff..2d88c6a 100644 --- a/tests/autofix.test.ts +++ b/tests/autofix.test.ts @@ -62,4 +62,18 @@ describe("autofix", () => { expect(result.output).toBe('read -r line\necho "$line"\n'); }); + + it("should apply fixes from multiple rules at once", () => { + const result = fix("read name\necho `pwd` $name\n", { + "bash/require-read-r": "error", + "bash/no-backticks": "error", + "bash/no-unquoted-expansions": "error", + }); + + expect(result.fixed).toBe(true); + + // The backticks become $(pwd) in the first pass, and the second + // pass quotes the now-unquoted substitution. + expect(result.output).toBe('read -r name\necho "$(pwd)" "$name"\n'); + }); }); diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index bfe0b75..d13754f 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -190,3 +190,93 @@ describe("recommended configuration", () => { ); }); }); + +describe("shipped rules", () => { + it("should run multiple rules together", () => { + const messages = lint( + [ + "cd /var/log", + "for f in $(ls); do", + " echo $f", + "done", + "cat error.log | grep error", + "", + ].join("\n"), + { + "bash/require-cd-guard": "error", + "bash/no-ls-iteration": "error", + "bash/no-unquoted-expansions": "error", + "bash/no-useless-cat": "error", + }, + ); + const ruleIds = new Set(messages.map(message => message.ruleId)); + + expect(ruleIds).toEqual( + new Set([ + "bash/no-ls-iteration", + "bash/no-unquoted-expansions", + "bash/no-useless-cat", + "bash/require-cd-guard", + ]), + ); + }); + + it("should report rules from the recommended config", () => { + const linter = new Linter(); + const messages = linter.verify( + "echo `pwd`\n", + [bash.configs.recommended] as never, + "script.sh", + ); + + expect( + messages.some(message => message.ruleId === "bash/no-backticks"), + ).toBe(true); + }); + + it("should flag a script with typical ShellCheck findings", () => { + const linter = new Linter(); + const code = [ + "#!/bin/bash", + "unused=1", + "cd /tmp", + "read input", + "echo $input", + "", + ].join("\n"); + const messages = linter.verify( + code, + [bash.configs.recommended] as never, + "script.sh", + ); + const ruleIds = new Set(messages.map(message => message.ruleId)); + + expect(ruleIds).toContain("bash/no-unused-vars"); + expect(ruleIds).toContain("bash/require-cd-guard"); + expect(ruleIds).toContain("bash/require-read-r"); + expect(ruleIds).toContain("bash/no-unquoted-expansions"); + }); + + it("should not report a well-written script", () => { + const linter = new Linter(); + const code = [ + "#!/bin/bash", + "set -euo pipefail", + 'cd "$(dirname "$0")" || exit', + "while read -r line; do", + " printf '%s\\n' \"$line\"", + "done < input.txt", + "for f in *.txt; do", + ' echo "$f"', + "done", + "", + ].join("\n"); + const messages = linter.verify( + code, + [bash.configs.recommended] as never, + "script.sh", + ); + + expect(messages).toEqual([]); + }); +}); From 2e537db83c189d9ec49c20b3b3f77dc95c76c9e8 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Thu, 8 Oct 2026 15:42:00 -0400 Subject: [PATCH 2/3] fix: use the shell plugin namespace and type names in integration-tests Co-Authored-By: Claude Opus 5.5 --- tests/autofix.test.ts | 6 +++--- tests/plugin.test.ts | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/autofix.test.ts b/tests/autofix.test.ts index 2d88c6a..568f6fd 100644 --- a/tests/autofix.test.ts +++ b/tests/autofix.test.ts @@ -65,9 +65,9 @@ describe("autofix", () => { it("should apply fixes from multiple rules at once", () => { const result = fix("read name\necho `pwd` $name\n", { - "bash/require-read-r": "error", - "bash/no-backticks": "error", - "bash/no-unquoted-expansions": "error", + "shell/require-read-r": "error", + "shell/no-backticks": "error", + "shell/no-unquoted-expansions": "error", }); expect(result.fixed).toBe(true); diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index d13754f..6ccbf61 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -203,20 +203,20 @@ describe("shipped rules", () => { "", ].join("\n"), { - "bash/require-cd-guard": "error", - "bash/no-ls-iteration": "error", - "bash/no-unquoted-expansions": "error", - "bash/no-useless-cat": "error", + "shell/require-cd-guard": "error", + "shell/no-ls-iteration": "error", + "shell/no-unquoted-expansions": "error", + "shell/no-useless-cat": "error", }, ); const ruleIds = new Set(messages.map(message => message.ruleId)); expect(ruleIds).toEqual( new Set([ - "bash/no-ls-iteration", - "bash/no-unquoted-expansions", - "bash/no-useless-cat", - "bash/require-cd-guard", + "shell/no-ls-iteration", + "shell/no-unquoted-expansions", + "shell/no-useless-cat", + "shell/require-cd-guard", ]), ); }); @@ -225,12 +225,12 @@ describe("shipped rules", () => { const linter = new Linter(); const messages = linter.verify( "echo `pwd`\n", - [bash.configs.recommended] as never, + [shell.configs.recommended] as never, "script.sh", ); expect( - messages.some(message => message.ruleId === "bash/no-backticks"), + messages.some(message => message.ruleId === "shell/no-backticks"), ).toBe(true); }); @@ -246,15 +246,15 @@ describe("shipped rules", () => { ].join("\n"); const messages = linter.verify( code, - [bash.configs.recommended] as never, + [shell.configs.recommended] as never, "script.sh", ); const ruleIds = new Set(messages.map(message => message.ruleId)); - expect(ruleIds).toContain("bash/no-unused-vars"); - expect(ruleIds).toContain("bash/require-cd-guard"); - expect(ruleIds).toContain("bash/require-read-r"); - expect(ruleIds).toContain("bash/no-unquoted-expansions"); + expect(ruleIds).toContain("shell/no-unused-vars"); + expect(ruleIds).toContain("shell/require-cd-guard"); + expect(ruleIds).toContain("shell/require-read-r"); + expect(ruleIds).toContain("shell/no-unquoted-expansions"); }); it("should not report a well-written script", () => { @@ -273,7 +273,7 @@ describe("shipped rules", () => { ].join("\n"); const messages = linter.verify( code, - [bash.configs.recommended] as never, + [shell.configs.recommended] as never, "script.sh", ); From 6779700e1f2b0aaa5d0b5c0571604d7929092932 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Thu, 8 Oct 2026 15:43:08 -0400 Subject: [PATCH 3/3] test: run the recommended rules with every shell language Co-Authored-By: Claude Opus 5.5 --- tests/plugin.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index 6ccbf61..1e41391 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -279,4 +279,20 @@ describe("shipped rules", () => { expect(messages).toEqual([]); }); + + it.each(["shell/bash", "shell/posix", "shell/mksh"])( + "should run the recommended rules with the %s language", + language => { + const linter = new Linter(); + const messages = linter.verify( + 'echo "`pwd`"\n', + [{ ...shell.configs.recommended, language }] as never, + "script.sh", + ); + + expect(messages.map(message => message.ruleId)).toEqual([ + "shell/no-backticks", + ]); + }, + ); });