Skip to content

Commit f1e1daa

Browse files
c33sNaktibalda
andauthored
Suggest most similar module in missing module exception
* added suggestion on missing modules * added one test * changed expectException method * changed hasModule to getModule * fixed exception message * Use string concatenation instead of variable interpolation Because it was hard to read. Co-authored-by: Gintautas Miselis <gintautas@miselis.lt>
1 parent 7b0db63 commit f1e1daa

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

src/Codeception/Lib/ModuleContainer.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ class ModuleContainer
2222
*/
2323
const MODULE_NAMESPACE = '\\Codeception\\Module\\';
2424

25+
/**
26+
* @var integer
27+
*/
28+
const MAXIMUM_LEVENSHTEIN_DISTANCE = 5;
29+
2530
public static $packages = [
2631
'AMQP' => 'codeception/module-amqp',
2732
'Apc' => 'codeception/module-apc',
@@ -275,12 +280,37 @@ public function hasModule($moduleName)
275280
public function getModule($moduleName)
276281
{
277282
if (!$this->hasModule($moduleName)) {
278-
throw new ModuleException(__CLASS__, "Module $moduleName couldn't be connected");
283+
$this->throwMissingModuleExceptionWithSuggestion(__CLASS__, $moduleName);
279284
}
280285

281286
return $this->modules[$moduleName];
282287
}
283288

289+
public function throwMissingModuleExceptionWithSuggestion($className, $moduleName)
290+
{
291+
$suggestedModuleNameInfo = $this->getModuleSuggestion($moduleName);
292+
throw new ModuleException($className, "Module $moduleName couldn't be connected" . $suggestedModuleNameInfo);
293+
}
294+
295+
protected function getModuleSuggestion($missingModuleName)
296+
{
297+
$shortestLevenshteinDistance = null;
298+
$suggestedModuleName = null;
299+
foreach ($this->modules as $moduleName => $module) {
300+
$levenshteinDistance = levenshtein($missingModuleName, $moduleName);
301+
if ($shortestLevenshteinDistance === null || $levenshteinDistance <= $shortestLevenshteinDistance) {
302+
$shortestLevenshteinDistance = $levenshteinDistance;
303+
$suggestedModuleName = $moduleName;
304+
}
305+
}
306+
307+
if ($suggestedModuleName !== null && $shortestLevenshteinDistance <= self::MAXIMUM_LEVENSHTEIN_DISTANCE) {
308+
return " (did you mean '$suggestedModuleName'?)";
309+
}
310+
311+
return '';
312+
}
313+
284314
/**
285315
* Get the module for an action.
286316
*

src/Codeception/Module.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ protected function getModules()
337337
protected function getModule($name)
338338
{
339339
if (!$this->hasModule($name)) {
340-
throw new Exception\ModuleException(__CLASS__, "Module $name couldn't be connected");
340+
$this->moduleContainer->throwMissingModuleExceptionWithSuggestion(__CLASS__, $name);
341341
}
342342
return $this->moduleContainer->getModule($name);
343343
}

tests/unit/Codeception/Lib/ModuleContainerTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace Codeception\Lib;
33

4+
use Codeception\Exception\ModuleException;
45
use Codeception\Lib\Interfaces\ConflictsWithModule;
56
use Codeception\Lib\Interfaces\DependsOnModule;
67
use Codeception\Test\Unit;
@@ -376,6 +377,23 @@ public function testInjectModuleIntoHelper()
376377
$this->moduleContainer->create('Codeception\Lib\HelperModule');
377378
$this->moduleContainer->hasModule('Codeception\Lib\HelperModule');
378379
}
380+
381+
public function testSuggestMissingModule()
382+
{
383+
$correctModule = 'Codeception\Lib\HelperModule';
384+
$wrongModule = 'Codeception\Lib\Helpamodule';
385+
386+
$config = ['modules' => [
387+
'enabled' => [$correctModule],
388+
]];
389+
$this->moduleContainer = new ModuleContainer(Stub::make('Codeception\Lib\Di'), $config);
390+
$this->moduleContainer->create('Codeception\Lib\HelperModule');
391+
392+
$message = "Codeception\Lib\ModuleContainer: Module $wrongModule couldn't be connected (did you mean '$correctModule'?)";
393+
$this->expectException('\Codeception\Exception\ModuleException');
394+
$this->expectExceptionMessage($message);
395+
$this->moduleContainer->getModule($wrongModule);
396+
}
379397
}
380398

381399
class StubModule extends \Codeception\Module

0 commit comments

Comments
 (0)