Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions src/Type/Pest/PestHookPropertyReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
Expand All @@ -18,6 +19,7 @@
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeFinder;

final class PestHookPropertyReader
Expand Down Expand Up @@ -136,7 +138,7 @@ private function extractUsesBeforeEachProperties(NodeFinder $nodeFinder, array $
}

foreach ($methodCall->getArgs() as $arg) {
if (! $arg->value instanceof Closure) {
if (! $arg->value instanceof Closure && ! $arg->value instanceof ArrowFunction) {
continue;
}

Expand Down Expand Up @@ -249,7 +251,7 @@ private function parseTestFile(string $filePath): array
}

foreach ($funcCall->getArgs() as $arg) {
if (! $arg->value instanceof Closure) {
if (! $arg->value instanceof Closure && ! $arg->value instanceof ArrowFunction) {
continue;
}

Expand All @@ -267,17 +269,18 @@ private function parseTestFile(string $filePath): array
* @param array<string, string> $useMap
* @return array<string, list<Expr>>
*/
private function extractPropertyAssignments(Closure $closure, array $useMap): array
private function extractPropertyAssignments(Closure|ArrowFunction $closure, array $useMap): array
{
$properties = [];
$localVarMap = $this->buildLocalVarExprMap($closure, $useMap);

foreach ($closure->stmts as $stmt) {
if (! $stmt instanceof Expression) {
continue;
}
foreach ($closure->getStmts() as $stmt) {
$expr = match (true) {
$stmt instanceof Expression => $stmt->expr,
$stmt instanceof Return_ => $stmt->expr,
default => null,
};

$expr = $stmt->expr;
if (! $expr instanceof Assign) {
continue;
}
Expand Down Expand Up @@ -335,11 +338,11 @@ private function extractPropertyAssignments(Closure $closure, array $useMap): ar
* @param array<string, string> $useMap
* @return array<string, Expr>
*/
private function buildLocalVarExprMap(Closure $closure, array $useMap): array
private function buildLocalVarExprMap(Closure|ArrowFunction $closure, array $useMap): array
{
$map = [];

foreach ($closure->stmts as $stmt) {
foreach ($closure->getStmts() as $stmt) {
if (! $stmt instanceof Expression) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

declare(strict_types=1);
2 changes: 2 additions & 0 deletions tests/Type/Fixtures/pesthook-scope/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
$this->scopedProperty = 'scoped';
})->in('Scoped');

pest()->extend(CustomTestCase::class)->beforeEach(fn (): string => $this->arrowScopedProperty = 'arrow-scoped')->in('ArrowScoped');

pest()->extend(CustomTestCase::class)->beforeEach(function (): void {
$this->untargetedProperty = 'untargeted';
});
12 changes: 12 additions & 0 deletions tests/Type/PestHookPropertyScopeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@
expect($reader->getPropertyExprs($dir.'/Sibling/SiblingTest.php'))->not->toHaveKey('scopedProperty');
});

test('an arrow function beforeEach scoped with in() applies to files under that target', function () use ($hooks): void {
[$dir, $reader] = $hooks();

expect($reader->getPropertyExprs($dir.'/ArrowScoped/ArrowScopedTest.php'))->toHaveKey('arrowScopedProperty');
});

test('an arrow function beforeEach scoped with in() does not leak into a sibling directory', function () use ($hooks): void {
[$dir, $reader] = $hooks();

expect($reader->getPropertyExprs($dir.'/Sibling/SiblingTest.php'))->not->toHaveKey('arrowScopedProperty');
});

test('a beforeEach without an in() target binds to no file at all', function () use ($hooks): void {
[$dir, $reader] = $hooks();

Expand Down
27 changes: 27 additions & 0 deletions tests/Type/data/test-hook-properties.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,33 @@ function testVarThisAnnotationDoesNotOverridePropertyType(): void
});
}

function testBeforeEachArrowFunctionAssignment(): void
{
beforeEach(fn () => $this->arrowPost = new Post);

it('resolves property type from an arrow function beforeEach', function (): void {
assertType(Post::class, $this->arrowPost);
});
}

function testBeforeEachArrowFunctionStringLiteral(): void
{
beforeEach(fn () => $this->arrowName = 'test');

it('resolves string type from an arrow function beforeEach', function (): void {
assertType("'test'", $this->arrowName);
});
}

function testBeforeEachArrowFunctionNonAssignmentStaysMixed(): void
{
beforeEach(fn () => someFunction());

it('returns mixed when an arrow function beforeEach does not assign a property', function (): void {
assertType('mixed', $this->neverAssigned);
});
}

function testBeforeEachSelfReferentialProperty(): void
{
beforeEach(function (): void {
Expand Down