Skip to content

Conversation

@johnbillion
Copy link
Member

Summary

  • ✅ Added comprehensive test infrastructure using @vscode/test-cli and mocha
  • ✅ Created 17 tests covering all major functionality
  • ✅ Refactored monolithic 618-line extension.ts into clean modular structure (now 14 lines!)
  • ✅ Updated CI/CD workflow to run tests
  • ✅ Updated documentation with test instructions

Test Coverage

The test suite includes:

  • Hook completion (actions and filters)
  • Callback completion (closures, arrow functions, utility functions)
  • Hover provider with documentation links
  • Edge cases (all hook functions, quotes, whitespace handling)

Breaking Changes

None - this is purely internal refactoring with comprehensive test coverage to ensure functionality remains unchanged.

johnbillion and others added 6 commits October 1, 2025 15:45
- 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>
Copy link

Copilot AI left a 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[] = [];

Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
if (params.length === 0) {
return [];
}

Copilot uses AI. Check for mistakes.
{
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);
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
const linePrefix = document.lineAt(position).text.substr(0, position.character);
const linePrefix = document.lineAt(position).text.slice(0, position.character);

Copilot uses AI. Check for mistakes.
'php',
{
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) {
const linePrefix = document.lineAt(position).text.substr(0, position.character);
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
const linePrefix = document.lineAt(position).text.substr(0, position.character);
const linePrefix = document.lineAt(position).text.substring(0, position.character);

Copilot uses AI. Check for mistakes.
- name: Run tests
run: xvfb-run -a npm test

- name: Run mutation testing
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
- name: Run mutation testing
- name: Run mutation testing
continue-on-error: true

Copilot uses AI. Check for mistakes.
out/test/**
test/**
.vscode-test.mjs
tsconfig.json
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
tsconfig.json
tsconfig.json
.mocharc.unit.json

Copilot uses AI. Check for mistakes.
docBlocksEnabled: boolean,
): vscode.CompletionItem {
const completionItemForArrow = new vscode.CompletionItem('Arrow function', vscode.CompletionItemKind.Function);

Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
if (!params.length || !params[0]?.variable) {
return completionItemForArrow;
}

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +49
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}`;
Copy link

Copilot AI Jan 3, 2026

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 */ }.

Suggested change
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}`;
}

Copilot uses AI. Check for mistakes.
'php',
{
provideHover(document, position) {
const linePrefix = document.lineAt(position).text.substr(0, position.character);
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
const linePrefix = document.lineAt(position).text.substr(0, position.character);
const linePrefix = document.lineAt(position).text.substring(0, position.character);

Copilot uses AI. Check for mistakes.
import * as vscode from 'vscode';

suite('Hook Completion Test Suite', () => {
vscode.window.showInformationMessage('Start hook completion tests.');
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
vscode.window.showInformationMessage('Start hook completion tests.');
suiteSetup(() => {
vscode.window.showInformationMessage('Start hook completion tests.');
});

Copilot uses AI. Check for mistakes.
out/test/**
test/**
.vscode-test.mjs
tsconfig.json
Copy link

Copilot AI Jan 3, 2026

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.

Suggested change
tsconfig.json
tsconfig.json
stryker.config.json

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants