Improve Support for PestPHP via IDE Helpers#532
Improve Support for PestPHP via IDE Helpers#532ace-of-aces wants to merge 26 commits intolaravel:mainfrom
Conversation
|
Hi @ace-of-aces Great job! Do I need to do something to make
Hmm for me it's working:
Maybe you’ve disabled indexing for the
This probably won't pass the CR. Did you try the https://github.com/laravel/vs-code-extension/blob/main/src/support/parser.ts#L143-L165 ? For example, the command: gives you: [
{
"type": "methodCall",
"methodName": "in",
"className": "pest",
"arguments": {
"type": "arguments",
"autocompletingIndex": 1,
"children": [
{
"type": "argument",
"name": null,
"children": [
{
"type": "string",
"value": "Feature",
"start": {
"line": 4,
"column": 9
},
"end": {
"line": 4,
"column": 16
}
}
]
}
]
}
},
{
"type": "methodCall",
"methodName": "use",
"className": "pest",
"arguments": {
"type": "arguments",
"autocompletingIndex": 1,
"children": [
{
"type": "argument",
"name": null,
"children": [
{
"type": "methodCall",
"methodName": null,
"className": "Illuminate\\Foundation\\Testing\\RefreshDatabase",
"arguments": {
"type": "arguments",
"autocompletingIndex": 0,
"children": []
},
"children": []
}
]
}
]
}
},
{
"type": "methodCall",
"methodName": "extend",
"className": "pest",
"arguments": {
"type": "arguments",
"autocompletingIndex": 1,
"children": [
{
"type": "argument",
"name": null,
"children": [
{
"type": "methodCall",
"methodName": null,
"className": "Tests\\TestCase",
"arguments": {
"type": "arguments",
"autocompletingIndex": 0,
"children": []
},
"children": []
}
]
}
]
}
},
{
"type": "methodCall",
"methodName": "pest",
"className": null,
"arguments": {
"type": "arguments",
"autocompletingIndex": 0,
"children": []
}
}
]That should be enough, right? |
|
Hey @N1ebieski, thanks for your feedback!
Hmm, that should work out of the box when using Intelephense AFAIK. What version of Intelephense are you running (for me it's the latest 1.16.3)? So, when there's no helper file present in the project,
Yeah, the part of the helper file that augments custom expectations and the custom TestCase extensions does work inside of I suspect that's because in the case of the functions, we're trying to override the
No, I didn't try that as I don't know enough about the vs-code-php-parser-cli yet, but looks like a more reliable approach!😄 |
|
Hi @ace-of-aces
You're right - the feature starts working after updating Intelephense to 1.16.3.
No. Auto-completion uses https://github.com/laravel/vs-code-extension/blob/main/src/support/parser.ts#L205-L230
|
🥳
@N1ebieski thx, it clicked for me too while currently working on it :) |
|
I've now replaced the regex-based approach via the php template with the AST-based detection through |
|
So... this took a bit more effort than I expected😅 But I think it should be ready for review now :) Current state
This should cover the entire configuration API of Pest AFAIK. What's I've not included (yet) are the helper method annotations for the Expectation API, mentioned above:
Looking forward to some more feedback on this! |
I’ve thought about this, and in my opinion it would be a good idea to move all these stubs (not only the The reason is that I (and probably other people as well) usually run PHPStan/Larastan in CI/CD workflows, and these stubs are required for that process. Related with #470 @TitasGailius Could you consider this suggestion? |
|
@N1ebieski thanks for the suggestion. I'm totally on board with moving those generated stubs to a path like |
|
Hey there! Having this as a package brings some advantages:
There are already some slight variations between my package and this PR. To avoid duplicating maintenance efforts or creating conflicting implementations for users, I’d love to find a middle ground. So here's my suggestion: Could we find a way for the vs-code-php-parser-cli to use this package, or perhaps have both projects share a common core? I absolutely understand if you don't want to have users auto-install a package from me as a random contributor tho :) Feel free to reach out. |
|
FYI, this is natively supported by |
|
@jakubmisek Not exactly.. Just tried it, but the heuristics your extension uses to determine the TestCase for Also it don't respect a user's
While I respect your efforts at DEVSENSE, I personally prefer an open source implementation where users can inspect things all the way from their code down to the declarations instead of having to rely on a black box :) |
|
@ace-of-aces, thank you very much for your response — I appreciate your thoughtful explanation. I initially noticed that the issue (#524) referenced intelephense (which is closed-source) and I was hoping to help address the false warning. Of course, providing a generated stub for the Pest test is a universal solution. It is also respected by DEVSENSE’s extension, as static analyzers generally cannot evaluate |
|
hey @ace-of-aces, what's the status of this PR? Is it ready to be reviewed or tested? Is there anything I can help with? |
|
Hi @TitasGailius, glad to hear back from you! I've tested this manually, so it should work in its current state. There are still some open questions for me:
So I think it would be great to get a clear picture on this before moving on with a full review :) |



closes #524
Since #524 got some upvotes, I decided to start working on this PR.
Summary
Essentially, this does two things:
_pest.phphelper file that contains declarations of Pests' function API and patches the$thisvariable via the@param-closure-thisdocblock tag, so the$thisvariable actually corresponds to the correct\Tests\TestCaseclass and doesn't lead to a bunch of "Undefined method" issues in Intelephense.Expectationclass and writes that also in_pest.phpI added a new
Laravel.pest.generateHelpersconfiguration flag for this, enabled by default.In action
Full autocompletion (for Laravel-specific Test methods and expectations), no LSP errors reported:
Screen.Recording.2026-01-09.at.17.06.11.mov
An example of a custom expectation having a proper definition:

Caveats / Open for discussion
There are some details to this implementation that are important understand beforehand.
_pest.phphelper file is not generated inside ofvendor/_laravel_ide_like the other helpers, because that won't be picked up by Intelephense. I suspect that's because Intelephense treats all vendor code with lower priority than user code. As the actual definition of Pest's functions are inside of the vendor directory alongside a helper file placed in_laravel_ide_, the helper file cannot override Pest source code.Moving the helper file into another file of the project directory is the only way I found to circumvent this.
Of course, this has the effect that the
.ide-helper/_pest.phpfile appears in version control, and users would need to decide whether to track it in Git or ignore it. As this could potentially lead to confusion for users, I'd love to have some other ideas here.@mixinPHPDoc tag on the main Expectation class ( the Mixins\Expecation class is where all of the built-in expectations are defined).Turns out that feature is only supported in the paid version of Intelephense, which I don't think most users of this extension have...
This could be mitigated by also having method hints in the PHPDoc of the helper file for all of Pest's built-in expectations. But then there's the question of how (internally parsing the mixin class and resolving all of its public methods, or just hard coding them for now)?
tests/Pest.phpconfiguration file inside a project with regexes. I've looked into how that's done via reflection for other helpers/php-templates, but those are all parsing PHP classes instead of top-level function calls like inPest.php. I've tried a bunch of configurations tho, and they all seem to get parsed properly and lead to the correct generation of helpers.self(which is the Expectation class). Actually figuring out what the custom expectation returns would require a lot more work, and probably something like the new Laravel Surveyor package :)I think this should be fine, as the official Pest docs recommend to always return the instance: "Of course, you probably want users to have the ability to "chain" expectations together with your custom expectation. To achieve this, ensure your custom expectation includes a return $this statement."
Looking forward to some feedback on this!❤️