* Dariusz RumiƄski * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Tests\Fixer\Import; use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException; use PhpCsFixer\Fixer\Import\OrderedImportsFixer; use PhpCsFixer\Tests\Test\AbstractFixerTestCase; /** * @internal * * @covers \PhpCsFixer\Fixer\Import\OrderedImportsFixer * * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Import\OrderedImportsFixer> * * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\Import\OrderedImportsFixer */ final class OrderedImportsFixerTest extends AbstractFixerTestCase { /** * @dataProvider provideFixCases * * @param _AutogeneratedInputConfiguration $configuration */ public function testFix(string $expected, ?string $input = null, array $configuration = []): void { $this->fixer->configure($configuration); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFixCases(): iterable { yield 'multiple namespaces' => [ <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } namespace BlaRoo { use Foo\Zar\Baz; use SomeClass; use Symfony\Annotation\Template; use Symfony\Doctrine\Entities\Entity, Zoo\Bar; class AnnotatedClass { /** * @Template(foobar=21) * @param Entity $foo */ public function doSomething($foo) { $bar = $foo->toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } EOF, <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } namespace BlaRoo { use Foo\Zar\Baz; use Zoo\Bar; use SomeClass; use Symfony\Annotation\Template, Symfony\Doctrine\Entities\Entity; class AnnotatedClass { /** * @Template(foobar=21) * @param Entity $foo */ public function doSomething($foo) { $bar = $foo->toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } EOF, ]; yield 'multiple namespaces II' => [ <<<'EOF' [ ' [ ' output ', ' output ', ]; yield 'with comment' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, ]; yield 'with traits' => [ <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, ]; yield 'with trait imports' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $baz) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $baz) {}; } } EOF, ]; yield 'with different cases' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Baz; use abc\Bar; [ <<<'EOF' [ <<<'EOF' [ <<<'EOF' [ '', '', ]; yield 'with comments' => [ ' [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar as ZooBar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar as ZooBar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, ]; yield 'with grouped import' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'const', 'function'], ], ]; yield 'with grouped import and comment' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'const', 'function'], ], ]; yield 'with grouped import for classes, constants and functions' => [ ' [ ' [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'function', 'const'], ], ]; yield 'with imports in the same line and in the new lines' => [ ' [ ' [ ' [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'function', 'const'], ], ]; yield 'alpha - [\'class\', \'const\', \'function\']' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'const', 'function'], ], ]; yield 'alpha - [\'function\', \'class\', \'const\']' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['function', 'class', 'const'], ], ]; yield 'alpha - [\'function\', \'const\', \'class\']' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['function', 'const', 'class'], ], ]; yield 'alpha - [\'const\', \'function\', \'class\']' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['const', 'function', 'class'], ], ]; yield 'alpha - [\'const\', \'class\', \'function\']' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['const', 'class', 'function'], ], ]; yield '"strcasecmp" vs. "strnatcasecmp"' => [ ' OrderedImportsFixer::SORT_ALPHA, ], ]; yield 'with trailing comma in single-line grouped import' => [ ' [ ' [ ' [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'with trailing comma in multi-line grouped import - sorting by length' => [ ' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'with trailing comma in multi-line grouped import - no sorting' => [ ' OrderedImportsFixer::SORT_NONE, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'only constant imports' => [ ' [ ' [ <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length with same length and case sensitive' => [ <<<'EOF' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, 'case_sensitive' => true, ], ]; yield 'by length with multiple namespace' => [ <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } namespace BlaRoo { use Zoo\Bar; use SomeClass; use Foo\Zar\Baz; use Symfony\Annotation\Template, Symfony\Doctrine\Entities\Entity; class AnnotatedClass { /** * @Template(foobar=21) * @param Entity $foo */ public function doSomething($foo) { $bar = $foo->toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } EOF, <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } namespace BlaRoo { use Foo\Zar\Baz; use Zoo\Bar; use SomeClass; use Symfony\Annotation\Template, Symfony\Doctrine\Entities\Entity; class AnnotatedClass { /** * @Template(foobar=21) * @param Entity $foo */ public function doSomething($foo) { $bar = $foo->toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length with comment' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length' => [ <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length with trait imports' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $baz) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $baz) {}; } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length with different cases' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Baz; use abc\Bar; OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length order with trailing digit' => [ <<<'EOF' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length code with imports only' => [ <<<'EOF' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length without uses' => [ <<<'EOF' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length 2' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar as ZooBar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar as ZooBar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, [ 'sort_algorithm' => OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'by length 3' => [ ' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'without types order' => [ ' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => null, ], ]; yield 'types order and length' => [ ' OrderedImportsFixer::SORT_LENGTH, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'types order and alphabet' => [ ' OrderedImportsFixer::SORT_ALPHA, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'types order and none' => [ ' OrderedImportsFixer::SORT_NONE, 'imports_order' => [OrderedImportsFixer::IMPORT_TYPE_CLASS, OrderedImportsFixer::IMPORT_TYPE_CONST, OrderedImportsFixer::IMPORT_TYPE_FUNCTION], ], ]; yield 'by none' => [ <<<'EOF' The normal use of this fixer should not change this sentence nor those statements below use Zoo\Bar as ZooBar; use Foo\Bar; use Foo\Zar\Baz; toArray(); /** @var ArrayInterface $bar */ return function () use ($bar, $foo) {}; } } EOF, null, [ 'sort_algorithm' => OrderedImportsFixer::SORT_NONE, 'imports_order' => null, ], ]; yield 'with case sensitive' => [ <<<'EOF' true, ], ]; } /** * @requires PHP <8.0 */ public function testFixPre80(): void { $this->doTest( ' $wrongConfig * * @dataProvider provideInvalidConfigurationCases */ public function testInvalidConfiguration(string $expectedMessage, array $wrongConfig): void { $this->expectException(InvalidFixerConfigurationException::class); $this->expectExceptionMessage($expectedMessage); $this->fixer->configure($wrongConfig); } /** * @return iterable */ public static function provideInvalidConfigurationCases(): iterable { yield 'unknown order types' => [ '[ordered_imports] Invalid configuration: Unknown sort types "foo" and "bar".', [ 'sort_algorithm' => OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'const', 'function', 'foo', 'bar'], ], ]; yield 'invalid order types size' => [ '[ordered_imports] Invalid configuration: Missing sort type "function".', [ 'sort_algorithm' => OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['class', 'const'], ], ]; yield 'invalid order type' => [ '[ordered_imports] Invalid configuration: Missing sort type "class".', [ 'sort_algorithm' => OrderedImportsFixer::SORT_ALPHA, 'imports_order' => ['const', 'function', 'bar'], ], ]; yield 'invalid sort algorithm' => [ '[ordered_imports] Invalid configuration: The option "sort_algorithm" with value "dope" is invalid. Accepted values are: "alpha", "length", "none".', [ 'sort_algorithm' => 'dope', 'imports_order' => null, ], ]; yield 'invalid sort algorithm 2' => [ '[ordered_imports] Invalid configuration: The option "sort_algorithm" with value array is invalid. Accepted values are: "alpha", "length", "none".', [ 'sort_algorithm' => [OrderedImportsFixer::SORT_ALPHA, OrderedImportsFixer::SORT_LENGTH], 'imports_order' => null, ], ]; yield 'invalid sort algorithm 3' => [ '[ordered_imports] Invalid configuration: The option "sort_algorithm" with value stdClass is invalid. Accepted values are: "alpha", "length", "none".', [ 'sort_algorithm' => new \stdClass(), 'imports_order' => null, ], ]; } }