Skip to content

Commit 269eae0

Browse files
authored
fix: resolve ::class constant in Pest describe() to FQN (#405)
1 parent 92307a4 commit 269eae0

2 files changed

Lines changed: 82 additions & 4 deletions

File tree

packages/phpunit/src/Interpreter/Resolvers/PestResolver.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { datasetExpander } from '../../TestParser/DatasetExpander';
2-
import type { AstNode, CallNode, ExpressionStatementNode } from '../AstParser/AstNode';
2+
import type {
3+
AstNode,
4+
CallNode,
5+
ClassConstantAccessNode,
6+
ExpressionStatementNode,
7+
} from '../AstParser/AstNode';
38
import { evaluate } from '../Expressions/PhpExpression';
49
import type { PHP } from '../PHP';
510
import type { FileInfo, PestCallDescriptor, Resolver } from '../types';
611
import { CallVisitor } from '../Visitors/CallVisitor';
12+
import { FQNResolver } from './FQNResolver';
713

814
const pestFunctionNames = new Set(['test', 'it', 'describe', 'arch']);
915

@@ -34,14 +40,26 @@ export class PestResolver implements Resolver {
3440
}
3541
}
3642

37-
function extractDescription(args: AstNode[]): string | undefined {
43+
function extractDescription(args: AstNode[], php: PHP): string | undefined {
3844
const firstArg = args[0];
3945
if (firstArg?.kind === 'string') {
4046
return firstArg.value;
4147
}
4248
if (firstArg?.kind === 'argument' && firstArg.value?.kind === 'string') {
4349
return firstArg.value.value;
4450
}
51+
if (firstArg?.kind === 'class_constant_access') {
52+
const node = firstArg as ClassConstantAccessNode;
53+
if (node.name === 'class') {
54+
return php.getResolver(FQNResolver).resolveFQN(node.scope);
55+
}
56+
}
57+
if (firstArg?.kind === 'argument' && firstArg.value?.kind === 'class_constant_access') {
58+
const node = firstArg.value as ClassConstantAccessNode;
59+
if (node.name === 'class') {
60+
return php.getResolver(FQNResolver).resolveFQN(node.scope);
61+
}
62+
}
4563
return undefined;
4664
}
4765

@@ -101,7 +119,7 @@ function buildPestCallDescriptor(call: CallNode, php: PHP): PestCallDescriptor |
101119

102120
return {
103121
fnName: rootCall.name,
104-
description: extractDescription(rootCall.arguments),
122+
description: extractDescription(rootCall.arguments, php),
105123
range: rootCall.loc,
106124
datasets: resolveDatasets(withSources),
107125
children: rootCall.name === 'describe' ? collectDescribeChildren(rootCall, php) : [],

packages/phpunit/src/TestParser/TestExtractorForPest.test.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,68 @@ it('generates name', function (object $user) {
581581
);
582582
});
583583

584+
it('describe with ::class using use statement', async () => {
585+
const content = `<?php
586+
587+
namespace Foo\\tests\\Unit;
588+
589+
use Foo\\Services\\PlaylistService;
590+
591+
describe(PlaylistService::class, function() {
592+
it('should detect this test', function() {
593+
expect(true)->toBeTrue();
594+
});
595+
});
596+
`;
597+
598+
expect(
599+
givenTest(
600+
file,
601+
content,
602+
'`Foo\\Services\\PlaylistService` → it should detect this test',
603+
),
604+
).toEqual(
605+
expect.objectContaining({
606+
type: TestType.method,
607+
id: 'tests/Fixtures/ExampleTest.php::`Foo\\Services\\PlaylistService` → it should detect this test',
608+
methodName: '`Foo\\Services\\PlaylistService` → it should detect this test',
609+
label: 'it should detect this test',
610+
file,
611+
}),
612+
);
613+
});
614+
615+
it('describe with ::class using fully qualified class name', async () => {
616+
const content = `<?php
617+
618+
namespace Foo\\tests\\Unit;
619+
620+
describe(\\Foo\\Services\\PlaylistService::class, function() {
621+
it('should detect this test', function() {
622+
expect(true)->toBeTrue();
623+
});
624+
});
625+
`;
626+
627+
expect(
628+
givenTest(
629+
file,
630+
content,
631+
'`Foo\\Services\\PlaylistService` → it should detect this test',
632+
),
633+
).toEqual(
634+
expect.objectContaining({
635+
type: TestType.method,
636+
id: 'tests/Fixtures/ExampleTest.php::`Foo\\Services\\PlaylistService` → it should detect this test',
637+
methodName: '`Foo\\Services\\PlaylistService` → it should detect this test',
638+
label: 'it should detect this test',
639+
file,
640+
}),
641+
);
642+
});
643+
584644
it('parse describe arch', () => {
585-
const content = `<?php
645+
const content = `<?php
586646
587647
describe('Given a project', function () {
588648
describe('When the architecture is tested', function () {

0 commit comments

Comments
 (0)