Fail the test workflow on test failures (#3197)

* Run pretest in CI to build all tests

* Alias paths when running tests

* Bundle ES modules with esbuild

* alias packages

* Preserve the test scripts exit code and display the output

* Remove outdated test
This commit is contained in:
Tomás Barreiro 2025-05-01 02:51:03 +02:00 committed by GitHub
parent d162a4b420
commit 13228ed46f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 80 additions and 24 deletions

View File

@ -81,7 +81,7 @@ jobs:
id: extension_coverage
continue-on-error: true
run: |
xvfb-run -a npm run test:coverage > extension_coverage.txt 2>&1 || true
xvfb-run -a npm run test:coverage > extension_coverage.txt 2>&1
PYTHONPATH=.github/scripts python -m coverage_check extract-coverage extension_coverage.txt --type=extension --github-output --verbose
# Run webview tests with coverage
@ -106,6 +106,18 @@ jobs:
webview-ui/webview_coverage.txt
retention-period: workflow # Artifacts are automatically deleted when the workflow completes
# Set the check as failed if any of the tests failed
- name: Check for test failures
run: |
# Check if any of the test steps failed
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#steps-context
if [ "${{ steps.extension_coverage.outcome }}" != "success" ] || [ "${{ steps.webview_coverage.outcome }}" != "success" ]; then
echo "Tests failed."
cat extension_coverage.txt
cat webview-ui/webview_coverage.txt
exit 1
fi
coverage:
needs: test
runs-on: ubuntu-latest

View File

@ -19,6 +19,7 @@ const aliasResolverPlugin = {
"@services": path.resolve(__dirname, "src/services"),
"@shared": path.resolve(__dirname, "src/shared"),
"@utils": path.resolve(__dirname, "src/utils"),
"@packages": path.resolve(__dirname, "src/packages"),
}
// For each alias entry, create a resolver

View File

@ -292,7 +292,7 @@
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
"protos": "node proto/build-proto.js && prettier src/shared/proto --write && prettier src/core/controller --write",
"compile-tests": "tsc -p ./tsconfig.test.json --outDir out",
"compile-tests": "node ./scripts/build-tests.js",
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"check-types": "tsc --noEmit",

61
scripts/build-tests.js Normal file
View File

@ -0,0 +1,61 @@
const { execSync } = require("child_process")
const esbuild = require("esbuild")
const watch = process.argv.includes("--watch")
/**
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
name: "esbuild-problem-matcher",
setup(build) {
build.onStart(() => {
console.log("[watch] build started")
})
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`)
console.error(` ${location.file}:${location.line}:${location.column}:`)
})
console.log("[watch] build finished")
})
},
}
const srcConfig = {
bundle: true,
minify: false,
sourcemap: true,
sourcesContent: true,
logLevel: "silent",
entryPoints: ["src/packages/**/*.ts"],
outdir: "out/packages",
format: "cjs",
platform: "node",
define: {
"process.env.IS_DEV": "true",
"process.env.IS_TEST": "true",
},
external: ["vscode"],
plugins: [esbuildProblemMatcherPlugin],
}
async function main() {
const srcCtx = await esbuild.context(srcConfig)
if (watch) {
await srcCtx.watch()
} else {
await srcCtx.rebuild()
await srcCtx.dispose()
}
}
execSync("tsc -p ./tsconfig.test.json --outDir out", { encoding: "utf-8" })
main().catch((e) => {
console.error(e)
process.exit(1)
})

View File

@ -76,26 +76,6 @@ describe("ModelContextTracker", () => {
}
})
it("should throw an error when controller is dereferenced", async () => {
// Create a new tracker with a controller that will be garbage collected
const weakTracker = new ModelContextTracker(mockContext, taskId)
// Force the WeakRef to return null by overriding the deref method
const weakRef = { deref: sandbox.stub().returns(null) }
sandbox.stub(WeakRef.prototype, "deref").callsFake(() => weakRef.deref())
try {
// Try to call the method - this should throw
await weakTracker.recordModelUsage("any-provider", "any-model", "any-mode")
// If we get here, the test should fail
expect.fail("Expected an error to be thrown")
} catch (error) {
// Verify the error message
expect(error.message).to.equal("Unable to access extension context")
}
})
it("should append model usage to existing entries", async () => {
// Add an existing model usage entry
const existingTimestamp = 1617200000000

View File

@ -6,7 +6,7 @@ import { fileExistsAtPath } from "@utils/fs"
import { ClineMessage } from "@shared/ExtensionMessage"
import { TaskMetadata } from "@core/context/context-tracking/ContextTrackerTypes"
import os from "os"
import { execa } from "execa"
import { execa } from "@packages/execa"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",

1
src/packages/execa.ts Normal file
View File

@ -0,0 +1 @@
export { execa } from "execa"

View File

@ -27,7 +27,8 @@
"@integrations/*": ["src/integrations/*"],
"@services/*": ["src/services/*"],
"@shared/*": ["src/shared/*"],
"@utils/*": ["src/utils/*"]
"@utils/*": ["src/utils/*"],
"@packages/*": ["src/packages/*"]
}
},
"include": ["src/**/*", "scripts/**/*"],