-
-
Notifications
You must be signed in to change notification settings - Fork 4
Add comprehensive test coverage and refactor extension #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
- Refactor codebase to separate pure logic from VS Code adapters - Create src/core/ directory with framework-agnostic code - Add Stryker mutation testing configuration - Add 51 unit tests for core modules - Achieve 63.70% mutation score (up from 5.48%) Test improvements: - typeHelpers: 0% → 62.50% (45 mutants killed) - snippetGenerator: 0% → 73.33% (55 mutants killed) - docblockGenerator: 0% → 62.79% (27 mutants killed) - hookHelpers: 0% → 56.58% (43 mutants killed) - matchers: 61.54% maintained (16 mutants killed) All 208 existing tests pass with zero regressions. 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a comprehensive refactoring that transforms a monolithic 618-line extension.ts file into a clean, modular architecture with extensive test coverage. The refactoring maintains backward compatibility through re-export wrapper modules while establishing a robust test infrastructure using @vscode/test-cli, mocha, and mutation testing with Stryker.
Key Changes:
- Refactored extension.ts from 618 lines to 14 lines by extracting functionality into dedicated modules
- Added comprehensive test suite with 17 tests covering hook completion, callback completion, hover providers, and edge cases
- Implemented mutation testing infrastructure with 50% mutation score threshold
- Updated CI/CD workflow to run tests automatically
Reviewed changes
Copilot reviewed 39 out of 41 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/extension.ts | Simplified to 14 lines - now only imports and registers providers |
| src/providers/*.ts | New provider modules for hook completion, callback completion, and hover functionality |
| src/core/*.ts | Core business logic extracted into dedicated modules (typeHelpers, snippetGenerator, matchers, hookHelpers, docblockGenerator) |
| src/utils/*.ts | Backward compatibility wrappers that re-export from core modules |
| src/types/index.ts | Centralized type definitions |
| src/test/suite/*.test.ts | Integration tests running in headless VS Code instance |
| src/test/unit/*.test.ts | Unit tests for core modules |
| stryker.config.json | Mutation testing configuration with 50% break threshold |
| .vscode-test.mjs | VS Code test runner configuration |
| .mocharc.unit.json | Mocha configuration for unit tests |
| package.json | Added test scripts and dev dependencies for testing infrastructure |
| CONTRIBUTING.md | Updated with new code structure documentation and test instructions |
| .github/workflows/test.yml | Added test execution and mutation testing steps to CI/CD |
| .vscodeignore | Added exclusions for test output directories |
| .eslintrc.json | Added test-specific linting rules |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| params: Array<{ types?: string[] }>, | ||
| ): vscode.CompletionItem[] { | ||
| const completions: vscode.CompletionItem[] = []; | ||
|
|
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The utility function completions assume params[0] exists without checking if the params array is empty. While filters typically have at least one parameter, this could cause a runtime error if a filter hook is defined without parameters. Consider adding a guard check: if (params.length === 0) return []; at the start of this function.
| if (params.length === 0) { | |
| return []; | |
| } |
| { | ||
| provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { | ||
| // get all text until the `position` and check if it reads a certain value and if so then complete | ||
| const linePrefix = document.lineAt(position).text.substr(0, position.character); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .substr() method is deprecated in favor of .substring() or .slice(). Consider updating to use the modern API.
| const linePrefix = document.lineAt(position).text.substr(0, position.character); | |
| const linePrefix = document.lineAt(position).text.slice(0, position.character); |
| 'php', | ||
| { | ||
| provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { | ||
| const linePrefix = document.lineAt(position).text.substr(0, position.character); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .substr() method is deprecated in favor of .substring() or .slice(). Consider updating to use the modern API.
| const linePrefix = document.lineAt(position).text.substr(0, position.character); | |
| const linePrefix = document.lineAt(position).text.substring(0, position.character); |
| - name: Run tests | ||
| run: xvfb-run -a npm test | ||
|
|
||
| - name: Run mutation testing |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mutation testing step in CI/CD may fail on the first run since it requires a mutation score of at least 50% (based on the 'break' threshold). Consider either making this step non-blocking initially (using 'continue-on-error: true') or adjusting the thresholds until the test suite is more mature.
| - name: Run mutation testing | |
| - name: Run mutation testing | |
| continue-on-error: true |
| out/test/** | ||
| test/** | ||
| .vscode-test.mjs | ||
| tsconfig.json |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .mocharc.unit.json file is missing from the .vscodeignore file. Since this is a test configuration file that's only needed during development, it should be excluded from the published extension to reduce package size.
| tsconfig.json | |
| tsconfig.json | |
| .mocharc.unit.json |
| docBlocksEnabled: boolean, | ||
| ): vscode.CompletionItem { | ||
| const completionItemForArrow = new vscode.CompletionItem('Arrow function', vscode.CompletionItemKind.Function); | ||
|
|
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The arrow function completion assumes params[0] exists without checking if the params array is empty. While filters typically have at least one parameter, this could cause a runtime error if a filter hook is defined without parameters. Consider adding a guard check: if (params.length === 0) return; or if (!params[0]?.variable) return; at the start of this function.
| if (!params.length || !params[0]?.variable) { | |
| return completionItemForArrow; | |
| } |
| const returnType = getReturnType(params[0]); | ||
|
|
||
| if (returnType) { | ||
| if (returnType.nullable) { | ||
| returnTypeString = ` : ?${returnType.type}`; | ||
| } else { | ||
| returnTypeString = ` : ${returnType.type}`; | ||
| } | ||
| } | ||
|
|
||
| snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; | ||
| documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The snippet generator assumes params[0] exists when generating filter callbacks without checking if the params array is empty. Lines 38 and 48 access params[0] directly. While filters typically have at least one parameter, this could cause a runtime error if a filter hook is defined without parameters. Consider adding a guard check at the start of the filter block: if (params.length === 0) { /* handle edge case */ }.
| const returnType = getReturnType(params[0]); | |
| if (returnType) { | |
| if (returnType.nullable) { | |
| returnTypeString = ` : ?${returnType.type}`; | |
| } else { | |
| returnTypeString = ` : ${returnType.type}`; | |
| } | |
| } | |
| snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; | |
| documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; | |
| if (params.length === 0) { | |
| snippetCallback = `() {\n\t\${1}\n}`; | |
| documentationCallback = `() {\n}`; | |
| } else { | |
| const returnType = getReturnType(params[0]); | |
| if (returnType) { | |
| if (returnType.nullable) { | |
| returnTypeString = ` : ?${returnType.type}`; | |
| } else { | |
| returnTypeString = ` : ${returnType.type}`; | |
| } | |
| } | |
| snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; | |
| documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; | |
| } |
| 'php', | ||
| { | ||
| provideHover(document, position) { | ||
| const linePrefix = document.lineAt(position).text.substr(0, position.character); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .substr() method is deprecated in favor of .substring() or .slice(). Consider updating to use the modern API.
| const linePrefix = document.lineAt(position).text.substr(0, position.character); | |
| const linePrefix = document.lineAt(position).text.substring(0, position.character); |
| import * as vscode from 'vscode'; | ||
|
|
||
| suite('Hook Completion Test Suite', () => { | ||
| vscode.window.showInformationMessage('Start hook completion tests.'); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The showInformationMessage call is executed at suite definition time, not during test execution. This will show the message every time the test file is loaded, which may not be the intended behavior. Consider moving this to a suiteSetup hook or removing it if it's not needed for debugging.
| vscode.window.showInformationMessage('Start hook completion tests.'); | |
| suiteSetup(() => { | |
| vscode.window.showInformationMessage('Start hook completion tests.'); | |
| }); |
| out/test/** | ||
| test/** | ||
| .vscode-test.mjs | ||
| tsconfig.json |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stryker.config.json file is missing from the .vscodeignore file. Since this is a mutation testing configuration file that's only needed during development, it should be excluded from the published extension to reduce package size.
| tsconfig.json | |
| tsconfig.json | |
| stryker.config.json |
Summary
Test Coverage
The test suite includes:
Breaking Changes
None - this is purely internal refactoring with comprehensive test coverage to ensure functionality remains unchanged.