From f9056b0740cb3f1608f5bdc3fc7ef1530b698a57 Mon Sep 17 00:00:00 2001 From: Enzo Innocenzi Date: Fri, 8 Nov 2024 17:52:12 +0100 Subject: [PATCH] feat(tests): support phpunit --- package.json | 6 ++-- src/commands/run-current-test-file.ts | 4 +-- src/commands/run-current-test.ts | 46 +++++++++++++++++++++------ src/commands/run-directory-tests.ts | 6 ++-- src/commands/run-tests.ts | 4 +-- src/utils/composer.ts | 31 ++++++++++++++---- src/utils/psr4.ts | 2 ++ src/utils/tests.ts | 22 +++++++------ 8 files changed, 86 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 89b6f55..a16632b 100644 --- a/package.json +++ b/package.json @@ -99,17 +99,17 @@ }, "hybridly.test.directory": { "type": "string", - "description": "Relative path to the test directory, if custom." + "description": "Relative path to the test directory, if custom. Supports Pest only." }, "hybridly.test.retry": { "type": "boolean", "default": true, - "description": "Prioritize previously-failed tests when running them." + "description": "Prioritize previously-failed tests when running them. Supports Pest only." }, "hybridly.test.bail": { "type": "boolean", "default": true, - "description": "Stop execution upon first non-passing test." + "description": "Stop execution upon first non-passing test. Supports Pest only." }, "hybridly.test.arguments": { "type": "string", diff --git a/src/commands/run-current-test-file.ts b/src/commands/run-current-test-file.ts index 2faeafd..4488a1e 100644 --- a/src/commands/run-current-test-file.ts +++ b/src/commands/run-current-test-file.ts @@ -1,11 +1,11 @@ import type { ExtensionContext } from '../context' import { registerPhpFileCommand } from '../utils/commands' -import { hasPest } from '../utils/composer' +import { hasTestRunner } from '../utils/composer' import { runTestsTask } from '../utils/tests' export async function registerRunCurrentTestFileCommand(context: ExtensionContext) { registerPhpFileCommand(context, 'run-current-test-file', async ({ editor }) => { - if (!hasPest(context.cwd)) { + if (!hasTestRunner(context.cwd)) { return } diff --git a/src/commands/run-current-test.ts b/src/commands/run-current-test.ts index b8961b1..eba1feb 100644 --- a/src/commands/run-current-test.ts +++ b/src/commands/run-current-test.ts @@ -1,27 +1,55 @@ import type { ExtensionContext } from '../context' import { registerPhpFileCommand } from '../utils/commands' -import { hasPest } from '../utils/composer' +import { getTestRunner, hasTestRunner } from '../utils/composer' import { runTestsTask } from '../utils/tests' export async function registerRunCurrentTestCommand(context: ExtensionContext) { registerPhpFileCommand(context, 'run-current-test', async ({ editor }) => { - if (!hasPest(context.cwd)) { + if (!hasTestRunner(context.cwd)) { return } let line = editor.selection.active.line let method: string | undefined + const testRunner = getTestRunner(context.cwd) - while (line > 0) { - const lineText = editor.document.lineAt(line).text - const match = lineText.match(/^\s*(?:it|test)\((.+['"])[,)]/m) + if (testRunner === 'pest') { + while (line > 0) { + const lineText = editor.document.lineAt(line).text + const match = lineText.match(/^\s*(?:it|test)\((.+['"])[,)]/m) - if (match) { - method = match[1] - break + if (match) { + method = match[1] + break + } + + line = line - 1 } + } + + if (testRunner === 'phpunit') { + while (line > 0) { + const lineText = editor.document.lineAt(line).text + const methodRE = /^\s*(?:public|private|protected)\s+function\s+(test_.+)\s*\(/m + + const testPrefixMatch = lineText.match(methodRE) + if (testPrefixMatch) { + method = testPrefixMatch[1] + break + } - line = line - 1 + if (lineText.match(/^\s*#\[Test(?:\(\))?\]/m)) { + const methodLine = editor.document.lineAt(line + 1).text + const methodMatch = methodLine.match(methodRE) + + if (methodMatch) { + method = methodMatch[1] + break + } + } + + line = line - 1 + } } const filter = method ? ` --filter ${method}` : '' diff --git a/src/commands/run-directory-tests.ts b/src/commands/run-directory-tests.ts index 4e7b739..6c1e985 100644 --- a/src/commands/run-directory-tests.ts +++ b/src/commands/run-directory-tests.ts @@ -1,15 +1,15 @@ import { dirname } from 'node:path' import type { ExtensionContext } from '../context' import { registerPhpFileCommand } from '../utils/commands' -import { hasPest } from '../utils/composer' +import { hasTestRunner } from '../utils/composer' import { runTestsTask } from '../utils/tests' export async function registerRunDirectoryTestsCommand(context: ExtensionContext) { registerPhpFileCommand(context, 'run-directory-tests', async (ctx) => { - if (!hasPest(context.cwd)) { + if (!hasTestRunner(context.cwd)) { return } - await runTestsTask(context.cwd, dirname(ctx.file.relativePath)) + await runTestsTask(context.cwd, dirname(ctx.file.fullPath)) }) } diff --git a/src/commands/run-tests.ts b/src/commands/run-tests.ts index f14e0b5..e7d5561 100644 --- a/src/commands/run-tests.ts +++ b/src/commands/run-tests.ts @@ -1,11 +1,11 @@ import type { ExtensionContext } from '../context' import { registerEditorCommand } from '../utils/commands' -import { hasPest } from '../utils/composer' +import { hasTestRunner } from '../utils/composer' import { runTestsTask } from '../utils/tests' export async function registerRunTestsCommand(context: ExtensionContext) { registerEditorCommand(context, 'php.run-tests', async () => { - if (!hasPest(context.cwd)) { + if (!hasTestRunner(context.cwd)) { return } diff --git a/src/utils/composer.ts b/src/utils/composer.ts index f6bc80b..e6d8896 100644 --- a/src/utils/composer.ts +++ b/src/utils/composer.ts @@ -3,7 +3,8 @@ import path from 'node:path' import { window } from 'vscode' import { log } from './log' -let hasLoggedPest = false +let testRunner: TestRunner +let hasLoggedTestRunner = false let hasLoggedComposer = false const caches = new Map() @@ -34,12 +35,28 @@ export function hasComposerPackage(cwd: string, pkg: string): boolean { return !!getComposer(cwd)?.require?.[pkg] || !!getComposer(cwd)?.['require-dev']?.[pkg] } -export function hasPest(cwd: string) { - if (!hasComposerPackage(cwd, 'pestphp/pest')) { - if (!hasLoggedPest) { - log.appendLine('Pest was not found in `composer.json`.') - window.showErrorMessage('Pest is not installed in this project.') - hasLoggedPest = true +export type TestRunner = 'pest' | 'phpunit' + +export function getTestRunner(cwd: string): TestRunner | undefined { + if (testRunner) { + return testRunner + } + + if (hasComposerPackage(cwd, 'pestphp/pest')) { + return testRunner = 'pest' + } + + if (hasComposerPackage(cwd, 'phpunit/phpunit')) { + return testRunner = 'phpunit' + } +} + +export function hasTestRunner(cwd: string) { + if (!getTestRunner(cwd)) { + if (!hasLoggedTestRunner) { + log.appendLine('Compatible test runner not found in `composer.json`.') + window.showErrorMessage('A compatible test runner is not installed in this project.') + hasLoggedTestRunner = true } return false diff --git a/src/utils/psr4.ts b/src/utils/psr4.ts index f8c11dd..c23d20a 100644 --- a/src/utils/psr4.ts +++ b/src/utils/psr4.ts @@ -16,6 +16,7 @@ export interface ControllerAction { } export interface PhpFile { + fullPath: string relativePath: string fileName: string className: string @@ -83,6 +84,7 @@ export async function resolvePhpFile(workspace: Uri, file: Uri): Promise