From 15509c4ab82f455271750e28b5c36f17fe13a3c3 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:05 +1000 Subject: [PATCH 01/38] Renamed the 'preg_match()' result array from '$match' to '$matches' in 'EntityFieldParser' and 'MappingTrait'. --- src/Backend/Core/Field/Parser/EntityFieldParser.php | 12 ++++++------ src/Steps/Web/MappingTrait.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Backend/Core/Field/Parser/EntityFieldParser.php b/src/Backend/Core/Field/Parser/EntityFieldParser.php index d2792f7a..10818c78 100644 --- a/src/Backend/Core/Field/Parser/EntityFieldParser.php +++ b/src/Backend/Core/Field/Parser/EntityFieldParser.php @@ -218,8 +218,8 @@ protected function detectCompoundMode(string $cell): bool { continue; } - if (preg_match('/[a-z_][a-z0-9_]*/A', $cell, $match, 0, $i) === 1) { - $j = $i + strlen($match[0]); + if (preg_match('/[a-z_][a-z0-9_]*/A', $cell, $matches, 0, $i) === 1) { + $j = $i + strlen($matches[0]); while ($j < $length && ($cell[$j] === ' ' || $cell[$j] === "\t")) { $j++; @@ -237,7 +237,7 @@ protected function detectCompoundMode(string $cell): bool { } } - $i += strlen($match[0]); + $i += strlen($matches[0]); continue; } @@ -425,7 +425,7 @@ protected function parseRecord(string $record, string $cell, int $base_offset): * [$key, $value] */ protected function parseColumn(string $column, string $cell, int $base_offset): array { - if (preg_match('/^([a-z_][a-z0-9_]*)\s*:\s*(.*)$/s', $column, $match) !== 1) { + if (preg_match('/^([a-z_][a-z0-9_]*)\s*:\s*(.*)$/s', $column, $matches) !== 1) { throw new ParseException( 'invalid_column', $base_offset, @@ -435,8 +435,8 @@ protected function parseColumn(string $column, string $cell, int $base_offset): ); } - $key = $match[1]; - $value_raw_with_ws = $match[2]; + $key = $matches[1]; + $value_raw_with_ws = $matches[2]; $value_raw = trim($value_raw_with_ws); // The value position inside $cell: column start + length consumed up to // the trimmed value's first character. diff --git a/src/Steps/Web/MappingTrait.php b/src/Steps/Web/MappingTrait.php index 15046a12..375df3ab 100644 --- a/src/Steps/Web/MappingTrait.php +++ b/src/Steps/Web/MappingTrait.php @@ -105,7 +105,7 @@ public function mappingTransformTable(TableNode $table): TableNode { * The string with every token replaced by its mapped value. */ public function mappingSubstitute(string $value): string { - $result = preg_replace_callback(self::MAPPING_TOKEN_REGEX, fn(array $match): string => $this->mappingGetValue(trim($match[1])), $value); + $result = preg_replace_callback(self::MAPPING_TOKEN_REGEX, fn(array $matches): string => $this->mappingGetValue(trim($matches[1])), $value); return $result ?? $value; } From 75abcb0372af532727dbcff85f5cc6292806d9d0 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:13 +1000 Subject: [PATCH 02/38] Spelled the 'UnserializableDomDocument' test double and 3 data set labels in American English. --- .../src/Unit/Backend/Core/Field/BooleanHandlerTest.php | 2 +- tests/phpunit/src/Unit/Backend/Core/Field/FileHandlerTest.php | 4 ++-- .../src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php | 2 +- tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/BooleanHandlerTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/BooleanHandlerTest.php index 3904f762..5ae3898f 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/BooleanHandlerTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/BooleanHandlerTest.php @@ -61,7 +61,7 @@ public static function dataProviderExpand(): \Iterator { NULL, ]; - yield 'unrecognised value rejected' => [ + yield 'unrecognized value rejected' => [ ['maybe'], NULL, \RuntimeException::class, diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/FileHandlerTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/FileHandlerTest.php index babe6c6d..b04a93aa 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/FileHandlerTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/FileHandlerTest.php @@ -114,13 +114,13 @@ public static function dataProviderExpand(): \Iterator { NULL, ]; - yield 'NULL target_id rejected by normalise' => [ + yield 'NULL target_id rejected by normalize' => [ [['target_id' => NULL]], NULL, \RuntimeException::class, 'File field "target_id" must not be NULL or empty.', ]; - yield 'empty target_id rejected by normalise' => [ + yield 'empty target_id rejected by normalize' => [ [['target_id' => '']], NULL, \RuntimeException::class, diff --git a/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php b/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php index 30574dd3..355bbe51 100644 --- a/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php @@ -476,7 +476,7 @@ public function testCleanupOptOut(string $value, bool $expected_cleanup): void { public static function dataProviderCleanupOptOut(): \Iterator { yield 'empty value still cleans up' => ['', TRUE]; - yield 'unrecognised value still cleans up' => ['maybe', TRUE]; + yield 'unrecognized value still cleans up' => ['maybe', TRUE]; yield 'zero still cleans up' => ['0', TRUE]; yield 'one disables cleanup' => ['1', FALSE]; yield 'true disables cleanup' => ['TRUE', FALSE]; diff --git a/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php index 56f3adcb..3c7f8ce6 100644 --- a/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php @@ -30,7 +30,7 @@ protected function setUp(): void { } public function testPrintLastResponseThrowsWhenSaveFails(): void { - $this->testObject->testSetDocument(new UnserialisableDomDocument(), ''); + $this->testObject->testSetDocument(new UnserializableDomDocument(), ''); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Failed to format the XML response.'); @@ -68,7 +68,7 @@ public function testSetDocument(\DOMDocument $document, string $content): void { /** * A document whose saveXML() always fails. */ -class UnserialisableDomDocument extends \DOMDocument { +class UnserializableDomDocument extends \DOMDocument { /** * {@inheritdoc} From 250e0812845da8ac34db8b485895b37322823126 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:21 +1000 Subject: [PATCH 03/38] Renamed the test identifiers that still called a registry or an authenticator a manager. --- .../Context/Initializer/BackendAwareInitializerTest.php | 2 +- .../src/Unit/Behat/Listener/BackendListenerTest.php | 2 +- .../src/Unit/Behat/Manager/BasicAuthenticatorTest.php | 8 ++++---- .../src/Unit/Behat/ServiceContainer/BackendPassTest.php | 2 +- .../src/Unit/Helper/Drupal/FixtureFileTraitTest.php | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/phpunit/src/Unit/Behat/Context/Initializer/BackendAwareInitializerTest.php b/tests/phpunit/src/Unit/Behat/Context/Initializer/BackendAwareInitializerTest.php index 4a0e595b..8d7c4d99 100644 --- a/tests/phpunit/src/Unit/Behat/Context/Initializer/BackendAwareInitializerTest.php +++ b/tests/phpunit/src/Unit/Behat/Context/Initializer/BackendAwareInitializerTest.php @@ -64,7 +64,7 @@ public function testBackendAwareContextReceivesEveryCollaborator(): void { $initializer->initializeContext($context); } - public function testUserAwareContextReceivesTheUserAndLoginManagers(): void { + public function testUserAwareContextReceivesTheUserRegistryAndAuthenticator(): void { $user_registry = $this->createMock(UserRegistryInterface::class); $authenticator = $this->createMock(AuthenticatorInterface::class); diff --git a/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php b/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php index 50c56f93..42161237 100644 --- a/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php +++ b/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php @@ -241,7 +241,7 @@ public static function dataProviderReplacedTagIsReported(): \Iterator { yield 'naming no backend' => [[], ['driver:'], 'The "@driver:" tag moved to "@backend:".']; } - public function testTheEnvironmentIsHandedToTheManager(): void { + public function testTheEnvironmentIsHandedToTheRegistry(): void { $event = $this->createEvent([], []); $backend_registry = $this->createMock(BackendRegistryInterface::class); diff --git a/tests/phpunit/src/Unit/Behat/Manager/BasicAuthenticatorTest.php b/tests/phpunit/src/Unit/Behat/Manager/BasicAuthenticatorTest.php index e475b10a..84e01765 100644 --- a/tests/phpunit/src/Unit/Behat/Manager/BasicAuthenticatorTest.php +++ b/tests/phpunit/src/Unit/Behat/Manager/BasicAuthenticatorTest.php @@ -39,7 +39,7 @@ public function testApplyBasicAuth(string $base_url, ?array $expected): void { $session->expects($this->once())->method('setBasicAuth')->with($expected[0], $expected[1]); } - $this->createManager($session, $base_url)->applyBasicAuth(); + $this->createBasicAuthenticator($session, $base_url)->applyBasicAuth(); } public static function dataProviderApplyBasicAuth(): \Iterator { @@ -75,7 +75,7 @@ public static function dataProviderApplyBasicAuth(): \Iterator { */ #[DataProvider('dataProviderFindCredentials')] public function testFindCredentials(string $base_url, ?array $expected): void { - $this->assertSame($expected, $this->createManager($this->createMock(Session::class), $base_url)->findCredentials()); + $this->assertSame($expected, $this->createBasicAuthenticator($this->createMock(Session::class), $base_url)->findCredentials()); } public static function dataProviderFindCredentials(): \Iterator { @@ -94,13 +94,13 @@ public function testApplyBasicAuthIgnoresUnsupportedDriver(): void { $session = $this->createMock(Session::class); $session->expects($this->once())->method('setBasicAuth')->willThrowException(new UnsupportedDriverActionException('Basic auth setup is not supported by %s', $this->createMock(DriverInterface::class))); - $this->createManager($session, 'http://alice:secret@localhost')->applyBasicAuth(); + $this->createBasicAuthenticator($session, 'http://alice:secret@localhost')->applyBasicAuth(); } /** * Builds an authenticator over a session and a configured base URL. */ - protected function createManager(Session $session, string $base_url): BasicAuthenticator { + protected function createBasicAuthenticator(Session $session, string $base_url): BasicAuthenticator { $mink = new Mink(['default' => $session]); $mink->setDefaultSessionName('default'); diff --git a/tests/phpunit/src/Unit/Behat/ServiceContainer/BackendPassTest.php b/tests/phpunit/src/Unit/Behat/ServiceContainer/BackendPassTest.php index 20f49082..767eaa22 100644 --- a/tests/phpunit/src/Unit/Behat/ServiceContainer/BackendPassTest.php +++ b/tests/phpunit/src/Unit/Behat/ServiceContainer/BackendPassTest.php @@ -21,7 +21,7 @@ #[CoversClass(BackendPass::class)] class BackendPassTest extends TestCase { - public function testWithoutTheManagerNothingIsProcessed(): void { + public function testWithoutTheRegistryNothingIsProcessed(): void { $container = new ContainerBuilder(); $container->setDefinition('behat_steps.backend.blackbox', (new Definition(BlackboxBackend::class))->addTag('behat_steps.backend', ['alias' => 'blackbox'])); diff --git a/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php b/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php index dfc4762a..96e90e1c 100644 --- a/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php @@ -410,10 +410,10 @@ public function getBackendRegistry(): BackendRegistryInterface { throw new \RuntimeException('Set the backend double before the helper reaches it.'); } - $manager = new BackendRegistry(['drupal' => $this->backend]); - $manager->setScenarioBackends(['drupal' => 'drupal']); + $backend_registry = new BackendRegistry(['drupal' => $this->backend]); + $backend_registry->setScenarioBackends(['drupal' => 'drupal']); - return $manager; + return $backend_registry; } /** From 7f3e0c9b7b63f62a25133c40778160a23c1caca6 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:29 +1000 Subject: [PATCH 04/38] Renamed the '$half' local in 'docs.php' to '$context', the name the generator gives the same directory elsewhere. --- docs.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs.php b/docs.php index 8aa5139b..b2937744 100644 --- a/docs.php +++ b/docs.php @@ -338,15 +338,15 @@ function collect_helper_traits(string $base_path = __DIR__): array { return $collected; } - foreach (scandir($helpers_path) ?: [] as $half) { - $half_path = $helpers_path . DIRECTORY_SEPARATOR . $half; + foreach (scandir($helpers_path) ?: [] as $context) { + $context_path = $helpers_path . DIRECTORY_SEPARATOR . $context; - if ($half === '.' || $half === '..' || !is_dir($half_path)) { + if ($context === '.' || $context === '..' || !is_dir($context_path)) { continue; } - foreach (scandir($half_path) ?: [] as $file) { - $file_path = $half_path . DIRECTORY_SEPARATOR . $file; + foreach (scandir($context_path) ?: [] as $file) { + $file_path = $context_path . DIRECTORY_SEPARATOR . $file; if (!is_file($file_path) || !file_declares_trait($file_path)) { continue; @@ -354,9 +354,9 @@ function collect_helper_traits(string $base_path = __DIR__): array { $short_name = basename($file, '.php'); /** @var class-string $trait_name */ - $trait_name = 'DrevOps\\BehatSteps\\Helper\\' . $half . '\\' . $short_name; + $trait_name = 'DrevOps\\BehatSteps\\Helper\\' . $context . '\\' . $short_name; - $collected[$short_name] = ['reflection' => new \ReflectionClass($trait_name), 'context' => $half]; + $collected[$short_name] = ['reflection' => new \ReflectionClass($trait_name), 'context' => $context]; } } @@ -1000,19 +1000,19 @@ function extract_helpers(array $class_names, array $exclude = [], string $base_p foreach (collect_helper_traits($base_path) as $trait_name => $collected) { $trait = $collected['reflection']; - $half = $collected['context']; + $context = $collected['context']; $helpers = collect_helper_methods($trait, NULL, NULL, helper_trait_contracts($trait, $class_names)); // @codeCoverageIgnoreStart if ($helpers === []) { continue; } // @codeCoverageIgnoreEnd - $name_contextual = ($half !== DEFAULT_CONTEXT ? $half . '\\' : '') . $trait_name; + $name_contextual = ($context !== DEFAULT_CONTEXT ? $context . '\\' : '') . $trait_name; $class_info = [ 'name' => $trait_name, 'name_contextual' => $name_contextual, - 'context' => $half, - 'source' => sprintf('%s/%s/%s.php', HELPERS_DIRECTORY, $half, $trait_name), + 'context' => $context, + 'source' => sprintf('%s/%s/%s.php', HELPERS_DIRECTORY, $context, $trait_name), 'steps_anchor' => NULL, 'helpers' => $helpers, ]; From 5fb46af185c104372991445c550e046b2b85b05b Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:38 +1000 Subject: [PATCH 05/38] Named the single-letter loop variables in 'AccessibilityTrait', 'WatchdogTrait' and 'BehatCliTrait' after the items they hold. --- src/Steps/Drupal/WatchdogTrait.php | 4 +- src/Steps/Web/AccessibilityTrait.php | 72 ++++++++++++------------- tests/behat/bootstrap/BehatCliTrait.php | 4 +- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Steps/Drupal/WatchdogTrait.php b/src/Steps/Drupal/WatchdogTrait.php index a9a13e5d..976a4f63 100644 --- a/src/Steps/Drupal/WatchdogTrait.php +++ b/src/Steps/Drupal/WatchdogTrait.php @@ -207,9 +207,9 @@ public function watchdogReadErrors(): array { } // Remove entries less severe than a warning. - foreach ($entries as $k => $error) { + foreach ($entries as $key => $error) { if ($error->severity > WATCHDOG_WARNING) { - unset($entries[$k]); + unset($entries[$key]); continue; } $error->variables = unserialize($error->variables); diff --git a/src/Steps/Web/AccessibilityTrait.php b/src/Steps/Web/AccessibilityTrait.php index b45c852e..534b73cf 100644 --- a/src/Steps/Web/AccessibilityTrait.php +++ b/src/Steps/Web/AccessibilityTrait.php @@ -368,15 +368,15 @@ protected function accessibilityEnforceGate(): void { $check_incomplete = $this->accessibilityEffectiveFailOnIncomplete(); $messages = []; - foreach ($this->accessibilityResults as $r) { - $display_url = $this->accessibilityFormatUrl((string) $r['url']); + foreach ($this->accessibilityResults as $result) { + $display_url = $this->accessibilityFormatUrl((string) $result['url']); - foreach ($this->accessibilityFilterViolations($r['result']['violations'] ?? [], $threshold) as $v) { - $messages[] = sprintf(' violation [%s] %s on %s', $v['impact'] ?? 'unknown', $v['id'] ?? '', $display_url); + foreach ($this->accessibilityFilterViolations($result['result']['violations'] ?? [], $threshold) as $violation) { + $messages[] = sprintf(' violation [%s] %s on %s', $violation['impact'] ?? 'unknown', $violation['id'] ?? '', $display_url); } if ($check_incomplete) { - foreach ($r['result']['incomplete'] ?? [] as $i) { - $messages[] = sprintf(' incomplete [%s] %s on %s', $i['impact'] ?? 'unknown', $i['id'] ?? '', $display_url); + foreach ($result['result']['incomplete'] ?? [] as $issue) { + $messages[] = sprintf(' incomplete [%s] %s on %s', $issue['impact'] ?? 'unknown', $issue['id'] ?? '', $display_url); } } } @@ -783,11 +783,11 @@ protected function accessibilityFilterViolations(array $violations, string $thre } $filtered = []; - foreach ($violations as $v) { - $impact = strtolower((string) ($v['impact'] ?? '')); + foreach ($violations as $violation) { + $impact = strtolower((string) ($violation['impact'] ?? '')); $pos = array_search($impact, $impacts, TRUE); if ($pos !== FALSE && $pos <= $threshold_pos) { - $filtered[] = $v; + $filtered[] = $violation; } } @@ -845,10 +845,10 @@ protected function accessibilityFormatGateMessage(string $url, string $rules, st sprintf('Accessibility gate failed on %s (rules: %s, threshold: %s, fail_on_incomplete: %s):', $this->accessibilityFormatUrl($url), $rules, $threshold, $check_incomplete ? 'yes' : 'no'), ]; - foreach ($violations as $v) { - $lines[] = sprintf(' violation [%s] %s - %s', $v['impact'] ?? 'unknown', $v['id'], $v['help']); - $lines[] = sprintf(' %s', $v['helpUrl']); - foreach ($v['nodes'] ?? [] as $node) { + foreach ($violations as $violation) { + $lines[] = sprintf(' violation [%s] %s - %s', $violation['impact'] ?? 'unknown', $violation['id'], $violation['help']); + $lines[] = sprintf(' %s', $violation['helpUrl']); + foreach ($violation['nodes'] ?? [] as $node) { $lines[] = sprintf(' -> %s', static::accessibilityStringifyTarget($node['target'] ?? [])); $html = trim((string) ($node['html'] ?? '')); if ($html !== '') { @@ -857,10 +857,10 @@ protected function accessibilityFormatGateMessage(string $url, string $rules, st } } - foreach ($incomplete as $i) { - $lines[] = sprintf(' incomplete [%s] %s - %s', $i['impact'] ?? 'unknown', $i['id'], $i['help']); - $lines[] = sprintf(' %s', $i['helpUrl']); - foreach ($i['nodes'] ?? [] as $node) { + foreach ($incomplete as $issue) { + $lines[] = sprintf(' incomplete [%s] %s - %s', $issue['impact'] ?? 'unknown', $issue['id'], $issue['help']); + $lines[] = sprintf(' %s', $issue['helpUrl']); + foreach ($issue['nodes'] ?? [] as $node) { $lines[] = sprintf(' -> %s', static::accessibilityStringifyTarget($node['target'] ?? [])); } } @@ -997,12 +997,12 @@ protected function accessibilityRenderHtmlPage(string $sections): string { protected function accessibilityRenderHtmlSections(): string { $body_sections = []; - foreach ($this->accessibilityResults as $r) { - $url = htmlspecialchars($this->accessibilityFormatUrl((string) $r['url']), ENT_QUOTES); - $rules = htmlspecialchars((string) $r['rules'], ENT_QUOTES); - $violations = $r['result']['violations'] ?? []; - $incomplete = $r['result']['incomplete'] ?? []; - $passes_count = count($r['result']['passes'] ?? []); + foreach ($this->accessibilityResults as $result) { + $url = htmlspecialchars($this->accessibilityFormatUrl((string) $result['url']), ENT_QUOTES); + $rules = htmlspecialchars((string) $result['rules'], ENT_QUOTES); + $violations = $result['result']['violations'] ?? []; + $incomplete = $result['result']['incomplete'] ?? []; + $passes_count = count($result['result']['passes'] ?? []); $section = sprintf('

%s

Rules: %s · %d violations · %d incomplete · %d passes

', $url, $rules, count($violations), count($incomplete), $passes_count); @@ -1075,23 +1075,23 @@ protected function accessibilityRenderJunit(): string { $total_tests = 0; $total_failures = 0; - foreach ($this->accessibilityResults as $r) { - $url = $this->accessibilityFormatUrl((string) $r['url']); - $violations = $r['result']['violations'] ?? []; - $passes = $r['result']['passes'] ?? []; + foreach ($this->accessibilityResults as $result) { + $url = $this->accessibilityFormatUrl((string) $result['url']); + $violations = $result['result']['violations'] ?? []; + $passes = $result['result']['passes'] ?? []; $cases_xml = ''; $tests = 0; $failures = 0; - foreach ($violations as $v) { - $failing = $this->accessibilityFilterViolations([$v], $threshold) !== []; - $rule_id = (string) ($v['id'] ?? 'unknown'); - $impact = (string) ($v['impact'] ?? 'unknown'); - $help = (string) ($v['help'] ?? ''); - $help_url = (string) ($v['helpUrl'] ?? ''); + foreach ($violations as $violation) { + $failing = $this->accessibilityFilterViolations([$violation], $threshold) !== []; + $rule_id = (string) ($violation['id'] ?? 'unknown'); + $impact = (string) ($violation['impact'] ?? 'unknown'); + $help = (string) ($violation['help'] ?? ''); + $help_url = (string) ($violation['helpUrl'] ?? ''); - foreach ($v['nodes'] ?? [] as $node) { + foreach ($violation['nodes'] ?? [] as $node) { $target = static::accessibilityStringifyTarget($node['target'] ?? []); $html = trim((string) ($node['html'] ?? '')); $details = sprintf("URL: %s\nRule: %s\nTarget: %s\nHTML: %s\nDocs: %s", $url, $rule_id, $target, $html, $help_url); @@ -1109,8 +1109,8 @@ protected function accessibilityRenderJunit(): string { } } - foreach ($passes as $p) { - $rule_id = (string) ($p['id'] ?? 'unknown'); + foreach ($passes as $pass) { + $rule_id = (string) ($pass['id'] ?? 'unknown'); $tests++; $cases_xml .= sprintf('', htmlspecialchars($rule_id, ENT_XML1 | ENT_QUOTES), htmlspecialchars($rule_id, ENT_XML1 | ENT_QUOTES)); } diff --git a/tests/behat/bootstrap/BehatCliTrait.php b/tests/behat/bootstrap/BehatCliTrait.php index 6f136c02..5e5f1e98 100644 --- a/tests/behat/bootstrap/BehatCliTrait.php +++ b/tests/behat/bootstrap/BehatCliTrait.php @@ -212,8 +212,8 @@ public function behatCliWriteScenarioSteps(PyStringNode $content, $tags = ''): v $content = strtr((string) $content, ["'''" => '"""']); $content_lines = explode(PHP_EOL, $content); - foreach ($content_lines as $k => $content_line) { - $content_lines[$k] = str_repeat(' ', 4) . trim($content_line); + foreach ($content_lines as $key => $content_line) { + $content_lines[$key] = str_repeat(' ', 4) . trim($content_line); } $content = implode(PHP_EOL, $content_lines); From d489f21679d78a7164d92ad48e8f015687473fac Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:48 +1000 Subject: [PATCH 06/38] Removed the space between 'fn' and its parameter list at the 9 arrow functions that had one. --- src/Backend/Core/Field/LinkHandler.php | 2 +- .../src/Unit/Backend/Core/Alias/AuthorAliasTest.php | 8 ++++---- .../src/Unit/Backend/Core/Alias/ParentTermAliasTest.php | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Backend/Core/Field/LinkHandler.php b/src/Backend/Core/Field/LinkHandler.php index a9a84ca2..e0d541d6 100644 --- a/src/Backend/Core/Field/LinkHandler.php +++ b/src/Backend/Core/Field/LinkHandler.php @@ -92,7 +92,7 @@ protected function doExpand(array $records): array { 'title' => $record['title'] ?? NULL, 'uri' => $record['uri'] ?? NULL, 'options' => [], - ], fn ($v): bool => $v !== NULL); + ], fn($v): bool => $v !== NULL); // UnroutedUrlAssembler::assemble() rejects a string 'options' value, so // query-string shorthand is parsed into an array. diff --git a/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php b/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php index 5612fa26..5b067cc5 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php @@ -25,7 +25,7 @@ class AuthorAliasTest extends TestCase { * Tests metadata accessors. */ public function testMetadataAccessors(): void { - $alias = new AuthorAlias(static fn (): ?object => NULL); + $alias = new AuthorAlias(static fn(): ?object => NULL); $this->assertInstanceOf(PreCreateAliasInterface::class, $alias); $this->assertSame('author', $alias->getName()); @@ -37,7 +37,7 @@ public function testMetadataAccessors(): void { * Tests that a known username resolves to 'uid' and removes 'author'. */ public function testApplyToStubResolvesKnownUser(): void { - $alias = new AuthorAlias(static fn (string $name): object => new FakeUser(42)); + $alias = new AuthorAlias(static fn(string $name): object => new FakeUser(42)); $stub = new EntityStub('node', 'article', ['title' => 'Hello', 'author' => 'alice']); @@ -52,7 +52,7 @@ public function testApplyToStubResolvesKnownUser(): void { * Tests that an unknown username throws and leaves the stub alone. */ public function testApplyToStubThrowsOnUnknownUser(): void { - $alias = new AuthorAlias(static fn (): ?object => NULL); + $alias = new AuthorAlias(static fn(): ?object => NULL); $stub = new EntityStub('node', 'article', ['author' => 'auther']); @@ -114,7 +114,7 @@ public static function dataProviderApplyToStubCoercesValueToString(): \Iterator */ #[DataProvider('dataProviderApplyToStubThrowsOnEmptyAuthor')] public function testApplyToStubThrowsOnEmptyAuthor(mixed $author): void { - $alias = new AuthorAlias(static fn (): object => new FakeUser(1)); + $alias = new AuthorAlias(static fn(): object => new FakeUser(1)); $stub = new EntityStub('node', 'article', ['author' => $author]); diff --git a/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php b/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php index 95d33941..bb5a8e92 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php @@ -24,7 +24,7 @@ class ParentTermAliasTest extends TestCase { * Tests metadata accessors. */ public function testMetadataAccessors(): void { - $alias = new ParentTermAlias(static fn (): ?int => NULL); + $alias = new ParentTermAlias(static fn(): ?int => NULL); $this->assertInstanceOf(PreCreateAliasInterface::class, $alias); $this->assertSame('parent', $alias->getName()); @@ -36,7 +36,7 @@ public function testMetadataAccessors(): void { * Tests that a resolved parent term replaces the value in place. */ public function testApplyToStubResolvesParent(): void { - $alias = new ParentTermAlias(static fn (string $name, string $vid): int => $name === 'Frameworks' && $vid === 'tags' ? 99 : 0); + $alias = new ParentTermAlias(static fn(string $name, string $vid): int => $name === 'Frameworks' && $vid === 'tags' ? 99 : 0); $stub = new EntityStub('taxonomy_term', 'tags', ['name' => 'Symfony', 'parent' => 'Frameworks']); @@ -68,7 +68,7 @@ public function testApplyToStubFallsBackToVidValue(): void { * Tests that unresolved parents throw and the value is left alone. */ public function testApplyToStubThrowsOnUnknownParent(): void { - $alias = new ParentTermAlias(static fn (): ?int => NULL); + $alias = new ParentTermAlias(static fn(): ?int => NULL); $stub = new EntityStub('taxonomy_term', 'tags', ['parent' => 'Nope']); @@ -90,7 +90,7 @@ public function testApplyToStubThrowsOnUnknownParent(): void { * bundle or 'vid' to scope the term lookup. */ public function testApplyToStubThrowsOnMissingVocabulary(): void { - $alias = new ParentTermAlias(static fn (): int => 1); + $alias = new ParentTermAlias(static fn(): int => 1); $stub = new EntityStub('taxonomy_term', NULL, ['parent' => 'Frameworks']); From ab2fdf7142d29faaee1e2b701e14fbacafdeabc9 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:51:56 +1000 Subject: [PATCH 07/38] Sorted the helper trait 'use' statements in 'ContentTrait', 'ParagraphsTrait' and 'EckTrait' alphabetically. --- src/Steps/Drupal/ContentTrait.php | 2 +- src/Steps/Drupal/EckTrait.php | 2 +- src/Steps/Drupal/ParagraphsTrait.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Steps/Drupal/ContentTrait.php b/src/Steps/Drupal/ContentTrait.php index eff5df7f..7c1d1ce6 100644 --- a/src/Steps/Drupal/ContentTrait.php +++ b/src/Steps/Drupal/ContentTrait.php @@ -44,9 +44,9 @@ */ trait ContentTrait { - use QueryTrait; use EntityLifecycleTrait; use FixtureFileTrait; + use QueryTrait; use TableTransposeTrait; /** diff --git a/src/Steps/Drupal/EckTrait.php b/src/Steps/Drupal/EckTrait.php index aaa2e09e..211ad02f 100644 --- a/src/Steps/Drupal/EckTrait.php +++ b/src/Steps/Drupal/EckTrait.php @@ -27,8 +27,8 @@ */ trait EckTrait { - use QueryTrait; use EntityLifecycleTrait; + use QueryTrait; /** * Create eck entities. diff --git a/src/Steps/Drupal/ParagraphsTrait.php b/src/Steps/Drupal/ParagraphsTrait.php index b102740c..d20f9acc 100644 --- a/src/Steps/Drupal/ParagraphsTrait.php +++ b/src/Steps/Drupal/ParagraphsTrait.php @@ -28,8 +28,8 @@ */ trait ParagraphsTrait { - use QueryTrait; use EntityLifecycleTrait; + use QueryTrait; /** * Create a paragraph of the given type with fields within an existing entity. From 7bbf7c61118fa382c19dd657e0b19734bf4119e5 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:52:06 +1000 Subject: [PATCH 08/38] Compared 'preg_match()' results with '=== 1' or '!== 1' at the 9 sites that tested them for truthiness. --- src/Backend/DrushBackend.php | 6 +++--- src/Steps/Web/CommandTrait.php | 2 +- src/Steps/Web/FileDownloadTrait.php | 4 ++-- src/Steps/Web/LinkTrait.php | 4 ++-- src/Steps/Web/ResponsiveTrait.php | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Backend/DrushBackend.php b/src/Backend/DrushBackend.php index 65b5f775..7aabd424 100644 --- a/src/Backend/DrushBackend.php +++ b/src/Backend/DrushBackend.php @@ -661,11 +661,11 @@ protected function resolveProjectDrush(string $fallback): string { * data row. */ protected function parseUserId(string $info): ?int { - if (preg_match('/User ID\s+:\s+(\d+)/', $info, $matches)) { + if (preg_match('/User ID\s+:\s+(\d+)/', $info, $matches) === 1) { return (int) $matches[1]; } - if (preg_match('/User ID/', $info)) { + if (preg_match('/User ID/', $info) === 1) { $lines = explode("\n", trim($info)); foreach ($lines as $line) { @@ -679,7 +679,7 @@ protected function parseUserId(string $info): ?int { continue; } - if (preg_match('/^\s*(\d+)\s/', $line, $matches)) { + if (preg_match('/^\s*(\d+)\s/', $line, $matches) === 1) { return (int) $matches[1]; } } diff --git a/src/Steps/Web/CommandTrait.php b/src/Steps/Web/CommandTrait.php index 696ec9a7..395ca959 100644 --- a/src/Steps/Web/CommandTrait.php +++ b/src/Steps/Web/CommandTrait.php @@ -374,7 +374,7 @@ protected function commandParseNumeric(string $value, string $label): float { * When the value is not an integer. */ protected function commandParseInteger(string $value, string $label): int { - if (!preg_match('/^-?\d+$/', $value)) { + if (preg_match('/^-?\d+$/', $value) !== 1) { throw new \RuntimeException(sprintf('The %s must be an integer, but got "%s".', $label, $value)); } diff --git a/src/Steps/Web/FileDownloadTrait.php b/src/Steps/Web/FileDownloadTrait.php index e7e5ea1d..ca46d50f 100644 --- a/src/Steps/Web/FileDownloadTrait.php +++ b/src/Steps/Web/FileDownloadTrait.php @@ -143,7 +143,7 @@ public function fileDownloadAssertFileContains(PyStringNode $string): void { if (is_array($lines)) { foreach ($lines as $line) { if ($this->fileDownloadIsRegex($string)) { - if (preg_match($string, $line)) { + if (preg_match($string, $line) === 1) { return; } } @@ -474,7 +474,7 @@ public function fileDownloadGetTempDir(): string { */ protected function fileDownloadIsRegex(string $string): bool { $string = trim($string); - return (bool) preg_match('/^\/.+\/[imsxADSUXJun]*$/', $string); + return preg_match('/^\/.+\/[imsxADSUXJun]*$/', $string) === 1; } /** diff --git a/src/Steps/Web/LinkTrait.php b/src/Steps/Web/LinkTrait.php index 6dc1d249..c33858b6 100644 --- a/src/Steps/Web/LinkTrait.php +++ b/src/Steps/Web/LinkTrait.php @@ -89,7 +89,7 @@ public function linkAssertExistsWithHrefWithinElement(string $link, string $href $pattern = '/' . preg_quote($href, '/') . '/'; $pattern = str_contains($href, '*') ? str_replace('\*', '.*', $pattern) : $pattern; - if (!preg_match($pattern, (string) $link_element->getAttribute('href'))) { + if (preg_match($pattern, (string) $link_element->getAttribute('href')) !== 1) { throw new ExpectationException(sprintf('The link href "%s" does not match the specified href "%s".', $link_element->getAttribute('href'), $href), $this->getSession()->getDriver()); } } @@ -144,7 +144,7 @@ public function linkAssertNotExistsWithHrefWithinElement(string $link, string $h $pattern = '/' . preg_quote($href, '/') . '/'; $pattern = str_contains($href, '*') ? str_replace('\*', '.*', $pattern) : $pattern; - if (preg_match($pattern, (string) $link_element->getAttribute('href'))) { + if (preg_match($pattern, (string) $link_element->getAttribute('href')) === 1) { throw new ExpectationException(sprintf('The link href "%s" matches the specified href "%s" but should not.', $link_element->getAttribute('href'), $href), $this->getSession()->getDriver()); } } diff --git a/src/Steps/Web/ResponsiveTrait.php b/src/Steps/Web/ResponsiveTrait.php index 4bde6269..bd636827 100644 --- a/src/Steps/Web/ResponsiveTrait.php +++ b/src/Steps/Web/ResponsiveTrait.php @@ -336,7 +336,7 @@ protected function responsiveFindTagBreakpoint(TaggedNodeInterface $node, string * If format is invalid. */ protected function responsiveExtractDimensions(string $dimensions, ?string $breakpoint = NULL): array { - if (!preg_match('/^(\d+)x(\d+)$/i', $dimensions, $matches)) { + if (preg_match('/^(\d+)x(\d+)$/i', $dimensions, $matches) !== 1) { if ($breakpoint) { throw new \RuntimeException(sprintf("Invalid breakpoint format for '%s': '%s'. Expected format: WIDTHxHEIGHT (e.g., 1920x1080)", $breakpoint, $dimensions)); } From 37ed82d0c08d59fdb1f007049fd1e236cfb1cc6f Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:56:13 +1000 Subject: [PATCH 09/38] Named the 5 outlying test bridge methods 'call()' after the protected method each exposes, like the other 20. --- .../Unit/Backend/DrushBackendMethodsTest.php | 4 ++-- .../Fixtures/ArgumentsExposingDrushBackend.php | 2 +- .../Helper/Drupal/FixtureFileTraitTest.php | 18 +++++++++--------- .../src/Unit/Steps/Drupal/EmailTraitTest.php | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php index a19376bf..58aaf0a6 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php @@ -297,7 +297,7 @@ protected function resolveSystemBinary(string $name): ?string { */ #[DataProvider('dataProviderParseArguments')] public function testParseArguments(array $options, array $expected): void { - $this->assertSame($expected, ArgumentsExposingDrushBackend::expose($options)); + $this->assertSame($expected, ArgumentsExposingDrushBackend::callParseArguments($options)); } /** @@ -311,7 +311,7 @@ public function testParseArgumentsRejectsName(string $name): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Invalid Drush option name: ' . $name); - ArgumentsExposingDrushBackend::expose([$name => 'value']); + ArgumentsExposingDrushBackend::callParseArguments([$name => 'value']); } /** diff --git a/tests/phpunit/src/Unit/Backend/Fixtures/ArgumentsExposingDrushBackend.php b/tests/phpunit/src/Unit/Backend/Fixtures/ArgumentsExposingDrushBackend.php index 0882b081..4b1df87b 100644 --- a/tests/phpunit/src/Unit/Backend/Fixtures/ArgumentsExposingDrushBackend.php +++ b/tests/phpunit/src/Unit/Backend/Fixtures/ArgumentsExposingDrushBackend.php @@ -23,7 +23,7 @@ class ArgumentsExposingDrushBackend extends DrushBackend { * @return array * The argv entries produced by 'parseArguments()'. */ - public static function expose(array $arguments): array { + public static function callParseArguments(array $arguments): array { return self::parseArguments($arguments); } diff --git a/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php b/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php index 96e90e1c..142a78c4 100644 --- a/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Drupal/FixtureFileTraitTest.php @@ -65,7 +65,7 @@ protected function createFixtureFiles(array $paths): void { #[DataProvider('dataProviderLooksLikeCompoundCell')] public function testLooksLikeCompoundCell(string $value, bool $expected): void { - $this->assertSame($expected, $this->testObject->callHelperLooksLikeCompoundCell($value)); + $this->assertSame($expected, $this->testObject->callLooksLikeCompoundCell($value)); } public static function dataProviderLooksLikeCompoundCell(): array { @@ -92,7 +92,7 @@ public function testExpandCompoundCellFixtures(string $value, array $existing_fi $this->testObject->managedBasenames = $existing_managed_basenames; $expected = str_replace('{FIXTURES}', $this->fixturesPath, $expected_template); - $actual = $this->testObject->callHelperExpandCompoundCellFixtures($value, $this->fixturesPath); + $actual = $this->testObject->callExpandCompoundCell($value, $this->fixturesPath); $this->assertSame($expected, $actual); } @@ -184,7 +184,7 @@ public function testExpandEntityFieldsFixtures(array $existing_fixture_files, ar $stub = new EntityStub('node', 'article', $stub_values); - $this->testObject->callHelperExpandEntityFieldsFixtures('node', $stub); + $this->testObject->callExpandEntityFields('node', $stub); $this->assertSame($expected_factory($this->fixturesPath), $stub->getValues()); } @@ -327,7 +327,7 @@ public function testNoFilesPathLeavesTheStubAlone(): void { $stub = new EntityStub('node', 'article', ['field_file' => 'document.pdf']); $this->testObject->minkFilesPath = ''; - $this->testObject->callHelperExpandEntityFieldsFixtures('node', $stub); + $this->testObject->callExpandEntityFields('node', $stub); $this->assertSame(['field_file' => 'document.pdf'], $stub->getValues()); } @@ -336,7 +336,7 @@ public function testMissingFilesDirectoryLeavesTheStubAlone(): void { $stub = new EntityStub('node', 'article', ['field_file' => 'document.pdf']); $this->testObject->minkFilesPath = $this->fixturesPath . 'no-such-directory'; - $this->testObject->callHelperExpandEntityFieldsFixtures('node', $stub); + $this->testObject->callExpandEntityFields('node', $stub); $this->assertSame(['field_file' => 'document.pdf'], $stub->getValues()); } @@ -348,7 +348,7 @@ public function testBackendWithoutTheCoreCapabilityLeavesTheStubAlone(): void { $this->testObject->backend = $this->createStub(BackendInterface::class); $this->testObject->minkFilesPath = rtrim($this->fixturesPath, DIRECTORY_SEPARATOR); - $this->testObject->callHelperExpandEntityFieldsFixtures('node', $stub); + $this->testObject->callExpandEntityFields('node', $stub); $this->assertSame(['field_file' => 'document.pdf'], $stub->getValues()); } @@ -383,15 +383,15 @@ class FixtureFileTraitTestImplementation extends WebRawContext { */ public ?BackendInterface $backend = NULL; - public function callHelperLooksLikeCompoundCell(string $value): bool { + public function callLooksLikeCompoundCell(string $value): bool { return $this->fixtureFileLooksLikeCompoundCell($value); } - public function callHelperExpandCompoundCellFixtures(string $value, string $fixture_path): string { + public function callExpandCompoundCell(string $value, string $fixture_path): string { return $this->fixtureFileExpandCompoundCell($value, $fixture_path); } - public function callHelperExpandEntityFieldsFixtures(string $entity_type, EntityStubInterface $stub): void { + public function callExpandEntityFields(string $entity_type, EntityStubInterface $stub): void { $this->fixtureFileExpandEntityFields($entity_type, $stub); } diff --git a/tests/phpunit/src/Unit/Steps/Drupal/EmailTraitTest.php b/tests/phpunit/src/Unit/Steps/Drupal/EmailTraitTest.php index cb20598b..0b6fe6c4 100644 --- a/tests/phpunit/src/Unit/Steps/Drupal/EmailTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Drupal/EmailTraitTest.php @@ -18,7 +18,7 @@ class EmailTraitTest extends UnitTestCase { #[DataProvider('dataProviderExtractLinks')] public function testExtractLinks(string $input, array $expected): void { - $result = EmailTraitTestImplementation::callEmailExtractLinks($input); + $result = EmailTraitTestImplementation::callExtractLinks($input); $this->assertSame($expected, $result); } @@ -103,7 +103,7 @@ class EmailTraitTestImplementation extends WebRawContext { * @return array * Array of extracted links. */ - public static function callEmailExtractLinks(string $string): array { + public static function callExtractLinks(string $string): array { return static::emailExtractLinks($string); } From e7d095c7f93a07eb322209579a7ab7523dddf6db Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:56:22 +1000 Subject: [PATCH 10/38] Renamed the '$trait_info' local in 'docs.php' to '$class_info', since the record also describes the toolbox classes. --- docs.php | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/docs.php b/docs.php index b2937744..15f60e96 100644 --- a/docs.php +++ b/docs.php @@ -1255,8 +1255,8 @@ function render_info(array $info, string $base_path = __DIR__, ?string $path_for $index_rows = []; - foreach ($info as $trait => $trait_info) { - $context = $trait_info['context']; + foreach ($info as $trait => $class_info) { + $context = $class_info['context']; // @phpstan-ignore-next-line $src_file = sprintf('%s/%s/%s.php', STEPS_DIRECTORY, $context, $trait); $src_file_path = $base_path . DIRECTORY_SEPARATOR . $src_file; @@ -1281,13 +1281,13 @@ function render_info(array $info, string $base_path = __DIR__, ?string $path_for // @phpstan-ignore-next-line $content_output[$context] ??= ''; // @phpstan-ignore-next-line - $content_output[$context] .= sprintf('## %s', $trait_info['name_contextual']) . PHP_EOL . PHP_EOL; + $content_output[$context] .= sprintf('## %s', $class_info['name_contextual']) . PHP_EOL . PHP_EOL; // @phpstan-ignore-next-line $content_output[$context] .= sprintf('[Source](%s), [Example](%s)', $src_file, $example_file) . PHP_EOL . PHP_EOL; $description_full = ''; // @phpstan-ignore-next-line - $lines = explode(PHP_EOL, $trait_info['description_full']); + $lines = explode(PHP_EOL, $class_info['description_full']); $was_list = FALSE; $in_code_block = FALSE; $code_block = ''; @@ -1339,23 +1339,23 @@ function render_info(array $info, string $base_path = __DIR__, ?string $path_for // @phpstan-ignore-next-line $content_output[$context] .= $description_full . PHP_EOL . PHP_EOL; // @phpstan-ignore-next-line - $content_output[$context] .= render_trait_prerequisites($trait_info['prerequisites'] ?? []); + $content_output[$context] .= render_trait_prerequisites($class_info['prerequisites'] ?? []); // @phpstan-ignore-next-line - $content_output[$context] .= render_trait_options($trait, $trait_info['options'] ?? []); + $content_output[$context] .= render_trait_options($trait, $class_info['options'] ?? []); // @phpstan-ignore-next-line - $index_rows_path = '#' . heading_anchor((string) $trait_info['name_contextual']); + $index_rows_path = '#' . heading_anchor((string) $class_info['name_contextual']); if ($path_for_links) { $index_rows_path = $path_for_links . $index_rows_path; } // @phpstan-ignore-next-line $index_rows[$context][] = [ // @phpstan-ignore-next-line - sprintf('[%s](%s)', $trait_info['name_contextual'], $index_rows_path), - $trait_info['description'], + sprintf('[%s](%s)', $class_info['name_contextual'], $index_rows_path), + $class_info['description'], ]; // @phpstan-ignore-next-line - foreach ($trait_info['methods'] as $method) { + foreach ($class_info['methods'] as $method) { $method['steps'] = is_array($method['steps']) ? $method['steps'] : [$method['steps']]; $method['description'] = is_string($method['description']) ? $method['description'] : ''; $method['example'] = is_string($method['example']) ? $method['example'] : ''; @@ -1443,18 +1443,18 @@ function render_helpers(array $info, string $base_path = __DIR__): string { $content_output = []; $index_rows = []; - foreach ($info as $trait_info) { - $context = (string) $trait_info['context']; - $name_contextual = (string) $trait_info['name_contextual']; + foreach ($info as $class_info) { + $context = (string) $class_info['context']; + $name_contextual = (string) $class_info['name_contextual']; $anchor = heading_anchor($name_contextual); - $helpers = is_array($trait_info['helpers']) ? $trait_info['helpers'] : []; + $helpers = is_array($class_info['helpers']) ? $class_info['helpers'] : []; - $src_file = (string) $trait_info['source']; + $src_file = (string) $class_info['source']; if (!file_exists($base_path . DIRECTORY_SEPARATOR . $src_file)) { throw new \Exception(sprintf('Source file %s does not exist', $base_path . DIRECTORY_SEPARATOR . $src_file)); } - $steps_anchor = $trait_info['steps_anchor'] ?? NULL; + $steps_anchor = $class_info['steps_anchor'] ?? NULL; $links = sprintf('[Source](%s)', $src_file); if (is_string($steps_anchor)) { $links .= sprintf(', [Steps](STEPS.md#%s)', $steps_anchor); @@ -1463,7 +1463,7 @@ function render_helpers(array $info, string $base_path = __DIR__): string { $content_output[$context] ??= ''; $content_output[$context] .= sprintf('## %s', $name_contextual) . PHP_EOL . PHP_EOL; $content_output[$context] .= $links . PHP_EOL . PHP_EOL; - $content_output[$context] .= '> ' . $trait_info['description'] . PHP_EOL . PHP_EOL; + $content_output[$context] .= '> ' . $class_info['description'] . PHP_EOL . PHP_EOL; foreach ($helpers as $helper) { $example = (string) $helper['example']; @@ -1484,7 +1484,7 @@ function render_helpers(array $info, string $base_path = __DIR__): string { $index_rows[$context][] = [ sprintf('[%s](#%s)', $name_contextual, $anchor), (string) count($helpers), - (string) $trait_info['description'], + (string) $class_info['description'], ]; } @@ -1518,10 +1518,10 @@ function render_helpers(array $info, string $base_path = __DIR__): string { function validate_helpers(array $info): array { $errors = []; - foreach ($info as $trait_info) { - $class_name = is_string($trait_info['name'] ?? NULL) ? $trait_info['name'] : ''; + foreach ($info as $class_info) { + $class_name = is_string($class_info['name'] ?? NULL) ? $class_info['name'] : ''; - foreach ((is_array($trait_info['helpers'] ?? NULL) ? $trait_info['helpers'] : []) as $helper) { + foreach ((is_array($class_info['helpers'] ?? NULL) ? $class_info['helpers'] : []) as $helper) { $name = is_string($helper['name'] ?? NULL) ? $helper['name'] : ''; $description = is_string($helper['description'] ?? NULL) ? $helper['description'] : ''; @@ -2158,18 +2158,18 @@ function validate_tags(array $info, string $base_path = __DIR__): array { $registry = tag_registry(); $errors = []; - foreach ($info as $trait => $trait_info) { - if (!is_array($trait_info)) { + foreach ($info as $trait => $class_info) { + if (!is_array($class_info)) { continue; } - $label = is_string($trait_info['name'] ?? NULL) ? $trait_info['name'] : (string) $trait; + $label = is_string($class_info['name'] ?? NULL) ? $class_info['name'] : (string) $trait; $texts = []; - if (is_string($trait_info['description_full'] ?? NULL)) { - $texts[] = $trait_info['description_full']; + if (is_string($class_info['description_full'] ?? NULL)) { + $texts[] = $class_info['description_full']; } - foreach ((is_array($trait_info['methods'] ?? NULL) ? $trait_info['methods'] : []) as $method) { + foreach ((is_array($class_info['methods'] ?? NULL) ? $class_info['methods'] : []) as $method) { if (is_array($method) && is_string($method['example'] ?? NULL)) { $texts[] = $method['example']; } From 86681521a330e9e2ef650a2eb74789585ddc678e Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:56:34 +1000 Subject: [PATCH 11/38] Doubled the backslashes in the 30 single-quoted namespace literals that used single ones, matching the rest of the codebase. --- tests/behat/bootstrap/BehatCliTrait.php | 14 ++++---- tests/behat/bootstrap/FeatureContextTrait.php | 2 +- .../src/PrerequisiteDeclarationsTest.php | 2 +- tests/phpunit/src/SkipGuardTest.php | 32 +++++++++---------- .../Behat/Config/TraitOptionResolverTest.php | 2 +- .../Unit/Behat/Context/ContextConfigTest.php | 2 +- .../src/Unit/Steps/OptionDeclarationsTest.php | 2 +- tests/phpunit/src/UnitTestCase.php | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/behat/bootstrap/BehatCliTrait.php b/tests/behat/bootstrap/BehatCliTrait.php index 5e5f1e98..3fe8dcbe 100644 --- a/tests/behat/bootstrap/BehatCliTrait.php +++ b/tests/behat/bootstrap/BehatCliTrait.php @@ -28,9 +28,9 @@ trait BehatCliTrait { * @var array */ protected const BEHAT_CLI_BASELINE_TRAITS = [ - 'Web\PathTrait', - 'Drupal\ContentTrait', - 'Drupal\UserTrait', + 'Web\\PathTrait', + 'Drupal\\ContentTrait', + 'Drupal\\UserTrait', ]; /** @@ -38,7 +38,7 @@ trait BehatCliTrait { * * @var array */ - protected const BEHAT_CLI_INHERENT_TRAITS = ['Helper\Drupal\AuthTrait', 'Helper\Drupal\StaticCacheTrait']; + protected const BEHAT_CLI_INHERENT_TRAITS = ['Helper\\Drupal\\AuthTrait', 'Helper\\Drupal\\StaticCacheTrait']; /** * Message selectors every generated configuration declares. @@ -400,9 +400,9 @@ public function behatCliAssertFailWithError(PyStringNode $message): void { // and an AssertionException where it is not. A non-assertion failure is // a \RuntimeException. $output = $this->getOutput(); - $has_valid_exception = str_contains((string) $output, ' (Behat\Mink\Exception\ExpectationException)') - || str_contains((string) $output, ' (Behat\Mink\Exception\ElementNotFoundException)') - || str_contains((string) $output, ' (DrevOps\BehatSteps\Exception\AssertionException)'); + $has_valid_exception = str_contains((string) $output, ' (Behat\\Mink\\Exception\\ExpectationException)') + || str_contains((string) $output, ' (Behat\\Mink\\Exception\\ElementNotFoundException)') + || str_contains((string) $output, ' (DrevOps\\BehatSteps\\Exception\\AssertionException)'); if (!$has_valid_exception) { throw new \RuntimeException('The output does not contain an assertion exception string as expected.'); } diff --git a/tests/behat/bootstrap/FeatureContextTrait.php b/tests/behat/bootstrap/FeatureContextTrait.php index 5439c161..2b5fa518 100644 --- a/tests/behat/bootstrap/FeatureContextTrait.php +++ b/tests/behat/bootstrap/FeatureContextTrait.php @@ -170,7 +170,7 @@ public function testAssertBackendOrder(string $order): void { */ #[Then('the :capability capability should resolve to the :expected backend')] public function testAssertCapabilityResolvesTo(string $capability, string $expected): void { - $interface = sprintf('DrevOps\BehatSteps\Backend\Capability\%sCapabilityInterface', $capability); + $interface = sprintf('DrevOps\\BehatSteps\\Backend\\Capability\\%sCapabilityInterface', $capability); if (!interface_exists($interface)) { throw new \RuntimeException(sprintf('There is no "%s" capability interface.', $capability)); diff --git a/tests/phpunit/src/PrerequisiteDeclarationsTest.php b/tests/phpunit/src/PrerequisiteDeclarationsTest.php index cb4d683d..feb5e7d7 100644 --- a/tests/phpunit/src/PrerequisiteDeclarationsTest.php +++ b/tests/phpunit/src/PrerequisiteDeclarationsTest.php @@ -23,7 +23,7 @@ class PrerequisiteDeclarationsTest extends UnitTestCase { /** * Namespace every capability a shipped trait declares belongs to. */ - protected const CAPABILITY_NAMESPACE = 'DrevOps\BehatSteps\Backend\Capability\\'; + protected const CAPABILITY_NAMESPACE = 'DrevOps\\BehatSteps\\Backend\\Capability\\'; /** * The methods that evaluate a trait's prerequisites. diff --git a/tests/phpunit/src/SkipGuardTest.php b/tests/phpunit/src/SkipGuardTest.php index d9129c23..55d7dd9a 100644 --- a/tests/phpunit/src/SkipGuardTest.php +++ b/tests/phpunit/src/SkipGuardTest.php @@ -30,21 +30,21 @@ class SkipGuardTest extends UnitTestCase { * Scenario hooks that carry no skip guard, and why. */ protected const UNGUARDED_HOOKS = [ - 'Helper\Drupal\StaticCacheTrait::staticCacheClear' => 'Clears the static caches the scenario filled.', - 'Steps\Drupal\ConfigTrait::configBeforeScenario' => 'Clears the snapshot registry.', - 'Steps\Drupal\StateTrait::stateBeforeScenario' => 'Clears the snapshot registry.', - 'Steps\Drupal\WatchdogTrait::watchdogAfterScenario' => 'Checks only a scenario whose start time watchdogSetScenario() set behind its guard.', - 'Steps\Web\AccessibilityTrait::accessibilityFinalizeScenario' => 'Reads the flag accessibilitySetupScenario() sets behind its guard.', - 'Steps\Web\CommandTrait::commandAfterScenario' => 'Clears the captured command output.', - 'Steps\Web\CommandTrait::commandBeforeScenario' => 'Clears the captured command output.', - 'Steps\Web\FieldTrait::fieldAfterScenario' => 'Clears the form validation registry.', - 'Steps\Web\JavascriptTrait::javascriptAfterScenario' => 'Reads the flag javascriptBeforeScenario() sets behind its guard.', - 'Steps\Web\JsonTrait::jsonAfterScenario' => 'Clears the decoded JSON.', - 'Steps\Web\JsonTrait::jsonBeforeScenario' => 'Clears the decoded JSON.', - 'Steps\Web\RandomTrait::randomAfterScenario' => 'Clears the resolved token values.', - 'Steps\Web\ResponsiveTrait::responsiveBeforeScenario' => 'Acts only on a "@breakpoint:" tag on the scenario or its feature.', - 'Steps\Web\XmlTrait::xmlAfterScenario' => 'Clears the loaded XML document.', - 'Steps\Web\XmlTrait::xmlBeforeScenario' => 'Clears the loaded XML document and the libxml error buffer.', + 'Helper\\Drupal\\StaticCacheTrait::staticCacheClear' => 'Clears the static caches the scenario filled.', + 'Steps\\Drupal\\ConfigTrait::configBeforeScenario' => 'Clears the snapshot registry.', + 'Steps\\Drupal\\StateTrait::stateBeforeScenario' => 'Clears the snapshot registry.', + 'Steps\\Drupal\\WatchdogTrait::watchdogAfterScenario' => 'Checks only a scenario whose start time watchdogSetScenario() set behind its guard.', + 'Steps\\Web\\AccessibilityTrait::accessibilityFinalizeScenario' => 'Reads the flag accessibilitySetupScenario() sets behind its guard.', + 'Steps\\Web\\CommandTrait::commandAfterScenario' => 'Clears the captured command output.', + 'Steps\\Web\\CommandTrait::commandBeforeScenario' => 'Clears the captured command output.', + 'Steps\\Web\\FieldTrait::fieldAfterScenario' => 'Clears the form validation registry.', + 'Steps\\Web\\JavascriptTrait::javascriptAfterScenario' => 'Reads the flag javascriptBeforeScenario() sets behind its guard.', + 'Steps\\Web\\JsonTrait::jsonAfterScenario' => 'Clears the decoded JSON.', + 'Steps\\Web\\JsonTrait::jsonBeforeScenario' => 'Clears the decoded JSON.', + 'Steps\\Web\\RandomTrait::randomAfterScenario' => 'Clears the resolved token values.', + 'Steps\\Web\\ResponsiveTrait::responsiveBeforeScenario' => 'Acts only on a "@breakpoint:" tag on the scenario or its feature.', + 'Steps\\Web\\XmlTrait::xmlAfterScenario' => 'Clears the loaded XML document.', + 'Steps\\Web\\XmlTrait::xmlBeforeScenario' => 'Clears the loaded XML document and the libxml error buffer.', ]; /** @@ -149,7 +149,7 @@ protected static function discoverScenarioHooks(): array { * Label a hook by its trait, relative to the library namespace, and method. */ protected static function hookLabel(string $trait, string $method): string { - return substr($trait, strlen('DrevOps\BehatSteps\\')) . '::' . $method; + return substr($trait, strlen('DrevOps\\BehatSteps\\')) . '::' . $method; } /** diff --git a/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php b/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php index 1dec0284..c391acab 100644 --- a/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php +++ b/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php @@ -22,7 +22,7 @@ class TraitOptionResolverTest extends UnitTestCase { /** * Context class the failure messages name. */ - protected const CONTEXT = 'Acme\Tests\SampleContext'; + protected const CONTEXT = 'Acme\\Tests\\SampleContext'; public function testEveryOptionStartsAtItsDeclaredDefault(): void { $resolver = $this->createResolver(); diff --git a/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php b/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php index 20fbb9ac..080b418a 100644 --- a/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php +++ b/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php @@ -140,7 +140,7 @@ public function testSkipTag(string $trait, array $tags, array $config, bool $exp public static function dataProviderSkipTag(): \Iterator { yield 'no tag and no config runs the hook' => ['SampleTrait', [], [], FALSE]; yield 'the trait tag skips the hook' => ['SampleTrait', ['behat-steps-skip:SampleTrait'], [], TRUE]; - yield 'a fully qualified trait reads the short tag' => ['Acme\Behat\SampleTrait', ['behat-steps-skip:SampleTrait'], [], TRUE]; + yield 'a fully qualified trait reads the short tag' => ['Acme\\Behat\\SampleTrait', ['behat-steps-skip:SampleTrait'], [], TRUE]; yield 'a disabled group skips the hook' => ['SampleTrait', [], ['sample' => ['enabled' => FALSE]], TRUE]; yield 'a hook tag does not skip the hook' => ['SampleTrait', ['behat-steps-skip:sampleBeforeScenario'], [], FALSE]; yield 'another trait tag does not skip the hook' => ['SampleTrait', ['behat-steps-skip:SampleExtraTrait'], [], FALSE]; diff --git a/tests/phpunit/src/Unit/Steps/OptionDeclarationsTest.php b/tests/phpunit/src/Unit/Steps/OptionDeclarationsTest.php index 14083092..5b1c360b 100644 --- a/tests/phpunit/src/Unit/Steps/OptionDeclarationsTest.php +++ b/tests/phpunit/src/Unit/Steps/OptionDeclarationsTest.php @@ -149,7 +149,7 @@ protected static function declaringTraits(): array { for ($class = new \ReflectionClass(DrupalContext::class); $class instanceof \ReflectionClass; $class = $class->getParentClass()) { foreach ($class->getTraits() as $trait) { - if (str_starts_with($trait->getName(), 'DrevOps\BehatSteps\Steps\\') && self::schemaMethods($trait->getName()) !== []) { + if (str_starts_with($trait->getName(), 'DrevOps\\BehatSteps\\Steps\\') && self::schemaMethods($trait->getName()) !== []) { $traits[] = $trait->getName(); } } diff --git a/tests/phpunit/src/UnitTestCase.php b/tests/phpunit/src/UnitTestCase.php index b0bd21ec..a8ccd69c 100644 --- a/tests/phpunit/src/UnitTestCase.php +++ b/tests/phpunit/src/UnitTestCase.php @@ -76,7 +76,7 @@ protected static function discoverTraits(): array { continue; } - $trait = 'DrevOps\BehatSteps\\' . str_replace(DIRECTORY_SEPARATOR, '\\', $relative); + $trait = 'DrevOps\\BehatSteps\\' . str_replace(DIRECTORY_SEPARATOR, '\\', $relative); if (!trait_exists($trait)) { continue; From b350b6062f3d5e9ff62f5117067adb62b22b8e20 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 11:59:26 +1000 Subject: [PATCH 12/38] Moved 'dataProviderParseArguments()' and 'dataProviderInvokesDrush()' directly after the tests they serve. --- .../Unit/Backend/DrushBackendMethodsTest.php | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php index 58aaf0a6..b890117c 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php @@ -300,6 +300,18 @@ public function testParseArguments(array $options, array $expected): void { $this->assertSame($expected, ArgumentsExposingDrushBackend::callParseArguments($options)); } + /** + * Data provider for 'testParseArguments()'. + */ + public static function dataProviderParseArguments(): \Iterator { + yield 'empty' => [[], []]; + yield 'single flag' => [['yes' => NULL], ['--yes']]; + yield 'single valued option' => [['format' => 'json'], ['--format=json']]; + yield 'flag and valued' => [['yes' => NULL, 'format' => 'json'], ['--yes', '--format=json']]; + yield 'multiple valued' => [['format' => 'json', 'root' => '/var/www'], ['--format=json', '--root=/var/www']]; + yield 'value carrying shell syntax stays one argument' => [['name' => '$(id)'], ['--name=$(id)']]; + } + /** * Tests 'parseArguments()' rejects an option name that is not a bare name. * @@ -324,18 +336,6 @@ public static function dataProviderParseArgumentsRejectsName(): \Iterator { yield 'slash' => ['some/path']; } - /** - * Data provider for 'testParseArguments()'. - */ - public static function dataProviderParseArguments(): \Iterator { - yield 'empty' => [[], []]; - yield 'single flag' => [['yes' => NULL], ['--yes']]; - yield 'single valued option' => [['format' => 'json'], ['--format=json']]; - yield 'flag and valued' => [['yes' => NULL, 'format' => 'json'], ['--yes', '--format=json']]; - yield 'multiple valued' => [['format' => 'json', 'root' => '/var/www'], ['--format=json', '--root=/var/www']]; - yield 'value carrying shell syntax stays one argument' => [['name' => '$(id)'], ['--name=$(id)']]; - } - /** * Tests every command-issuing method drives 'drush()' as expected. * @@ -362,6 +362,38 @@ public function testInvokesDrush(string $method, array $args, ?string $expected_ } } + /** + * Data provider: method -> args -> first-expected-drush-command. + */ + public static function dataProviderInvokesDrush(): \Iterator { + $user = new EntityStub('user', NULL, ['name' => 'alice', 'pass' => 'pw', 'mail' => 'alice@ex.co']); + + yield 'userCreate' => ['userCreate', [$user], 'user-create', "User ID : 9\n"]; + yield 'userDelete' => ['userDelete', [$user], 'user-cancel']; + yield 'userAddRole' => ['userAddRole', [$user, 'admin'], 'user-add-role']; + yield 'cronRun' => ['cronRun', [], 'cron']; + yield 'moduleInstall' => ['moduleInstall', ['dblog'], 'pm-enable']; + yield 'moduleUninstall' => ['moduleUninstall', ['dblog'], 'pm-uninstall']; + yield 'configGet' => ['configGet', ['system.site', 'name'], 'config:get', '"Example"']; + yield 'configGetOriginal' => ['configGetOriginal', ['system.site'], 'config:get', '{}']; + yield 'configSet' => ['configSet', ['system.site', 'name', 'v'], 'config:set']; + yield 'configExists' => ['configExists', ['system.site'], 'config:get', '{}']; + yield 'configGetData' => ['configGetData', ['system.site'], 'config:get', '{"name":"Example"}']; + yield 'configSetData' => ['configSetData', ['system.site', ['name' => 'Example']], 'config:get', '{"name":"Old"}']; + yield 'configDelete' => ['configDelete', ['system.site'], 'config:get', '{}']; + yield 'stateGet' => ['stateGet', ['my.key'], 'state:get', '{"my.key":"v"}']; + yield 'stateSet' => ['stateSet', ['my.key', 'v'], 'state:set']; + yield 'stateDelete' => ['stateDelete', ['my.key'], 'state:delete']; + yield 'stateExists' => ['stateExists', ['my.key'], 'state:get', '{"my.key":"v"}']; + yield 'moduleIsEnabled' => ['moduleIsEnabled', ['dblog'], 'pm:list', '{"dblog":{"status":"Enabled"}}']; + yield 'moduleIsPresent' => ['moduleIsPresent', ['dblog'], 'pm:list', '{"dblog":{"status":"Disabled"}}']; + yield 'roleCreate no permissions' => ['roleCreate', [[]], 'role:create']; + yield 'roleCreate with permissions' => ['roleCreate', [['access content']], 'role:create']; + yield 'roleCreate with explicit id' => ['roleCreate', [[], 'editor'], 'role:create']; + yield 'roleCreate with id and label' => ['roleCreate', [['access content'], 'editor', 'Editor'], 'role:create']; + yield 'roleDelete' => ['roleDelete', ['editor'], 'role:delete']; + } + /** * Tests that a config write hands Drush a format it actually parses. * @@ -543,38 +575,6 @@ public function testConfigSetDataKeepsAnEmptyObjectInPlace(): void { $this->assertSame('config:set', end($commands)); } - /** - * Data provider: method -> args -> first-expected-drush-command. - */ - public static function dataProviderInvokesDrush(): \Iterator { - $user = new EntityStub('user', NULL, ['name' => 'alice', 'pass' => 'pw', 'mail' => 'alice@ex.co']); - - yield 'userCreate' => ['userCreate', [$user], 'user-create', "User ID : 9\n"]; - yield 'userDelete' => ['userDelete', [$user], 'user-cancel']; - yield 'userAddRole' => ['userAddRole', [$user, 'admin'], 'user-add-role']; - yield 'cronRun' => ['cronRun', [], 'cron']; - yield 'moduleInstall' => ['moduleInstall', ['dblog'], 'pm-enable']; - yield 'moduleUninstall' => ['moduleUninstall', ['dblog'], 'pm-uninstall']; - yield 'configGet' => ['configGet', ['system.site', 'name'], 'config:get', '"Example"']; - yield 'configGetOriginal' => ['configGetOriginal', ['system.site'], 'config:get', '{}']; - yield 'configSet' => ['configSet', ['system.site', 'name', 'v'], 'config:set']; - yield 'configExists' => ['configExists', ['system.site'], 'config:get', '{}']; - yield 'configGetData' => ['configGetData', ['system.site'], 'config:get', '{"name":"Example"}']; - yield 'configSetData' => ['configSetData', ['system.site', ['name' => 'Example']], 'config:get', '{"name":"Old"}']; - yield 'configDelete' => ['configDelete', ['system.site'], 'config:get', '{}']; - yield 'stateGet' => ['stateGet', ['my.key'], 'state:get', '{"my.key":"v"}']; - yield 'stateSet' => ['stateSet', ['my.key', 'v'], 'state:set']; - yield 'stateDelete' => ['stateDelete', ['my.key'], 'state:delete']; - yield 'stateExists' => ['stateExists', ['my.key'], 'state:get', '{"my.key":"v"}']; - yield 'moduleIsEnabled' => ['moduleIsEnabled', ['dblog'], 'pm:list', '{"dblog":{"status":"Enabled"}}']; - yield 'moduleIsPresent' => ['moduleIsPresent', ['dblog'], 'pm:list', '{"dblog":{"status":"Disabled"}}']; - yield 'roleCreate no permissions' => ['roleCreate', [[]], 'role:create']; - yield 'roleCreate with permissions' => ['roleCreate', [['access content']], 'role:create']; - yield 'roleCreate with explicit id' => ['roleCreate', [[], 'editor'], 'role:create']; - yield 'roleCreate with id and label' => ['roleCreate', [['access content'], 'editor', 'Editor'], 'role:create']; - yield 'roleDelete' => ['roleDelete', ['editor'], 'role:delete']; - } - /** * Creates a backend with a stubbed 'drush()' that records every invocation. */ From 394310cc84b4d95d2b86a5e5d3b78cbf58456c79 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 12:02:58 +1000 Subject: [PATCH 13/38] Moved the helper methods of 'AuthenticatorTest' and 'DrushBackendMethodsTest' after their last test, like the other test classes. --- .../Unit/Backend/DrushBackendMethodsTest.php | 26 ++++---- .../Unit/Behat/Manager/AuthenticatorTest.php | 64 +++++++++---------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php index b890117c..c869ee16 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php @@ -274,19 +274,6 @@ public function testDrushThrowsRuntimeExceptionOnFailure(): void { $backend->drush('version'); } - /** - * Returns the first executable location for a system utility, or NULL. - */ - protected function resolveSystemBinary(string $name): ?string { - foreach (['/bin/' . $name, '/usr/bin/' . $name] as $candidate) { - if (is_executable($candidate)) { - return $candidate; - } - } - - return NULL; - } - /** * Tests 'parseArguments()' serialises boolean and value options. * @@ -575,6 +562,19 @@ public function testConfigSetDataKeepsAnEmptyObjectInPlace(): void { $this->assertSame('config:set', end($commands)); } + /** + * Returns the first executable location for a system utility, or NULL. + */ + protected function resolveSystemBinary(string $name): ?string { + foreach (['/bin/' . $name, '/usr/bin/' . $name] as $candidate) { + if (is_executable($candidate)) { + return $candidate; + } + } + + return NULL; + } + /** * Creates a backend with a stubbed 'drush()' that records every invocation. */ diff --git a/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php b/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php index 1d7c26ff..ebec5ebb 100644 --- a/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php +++ b/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php @@ -486,38 +486,6 @@ public function testGetLogoutElement(): void { $this->assertSame($link, $authenticator->getLogoutElement()); } - protected function createSessionMock(?DocumentElement $page = NULL): Session { - $session = $this->createMock(Session::class); - $session->method('getPage')->willReturn($page ?? $this->createMock(DocumentElement::class)); - $session->method('getDriver')->willReturn($this->createMock(DriverInterface::class)); - return $session; - } - - /** - * Creates a mock for the AuthenticationCapability and BackendInterface. - * - * @return \DrevOps\BehatSteps\Backend\Capability\AuthenticationCapabilityInterface&\DrevOps\BehatSteps\Backend\BackendInterface&\PHPUnit\Framework\MockObject\MockObject - * The mocked backend. - */ - protected function createAuthBackendMock(): AuthenticationCapabilityInterface&BackendInterface&MockObject { - /** @var \DrevOps\BehatSteps\Backend\Capability\AuthenticationCapabilityInterface&\DrevOps\BehatSteps\Backend\BackendInterface&\PHPUnit\Framework\MockObject\MockObject $backend */ - $backend = $this->createMockForIntersectionOfInterfaces([ - AuthenticationCapabilityInterface::class, - BackendInterface::class, - ]); - $backend->method('isBootstrapped')->willReturn(TRUE); - return $backend; - } - - protected function createBackendRegistryMock(): BackendRegistryInterface { - $backend = $this->createMock(BackendInterface::class); - $backend->method('isBootstrapped')->willReturn(TRUE); - $backend_registry = $this->createMock(BackendRegistryInterface::class); - $backend_registry->method('hasCapability')->willReturn(FALSE); - $backend_registry->method('getBackend')->willReturn($backend); - return $backend_registry; - } - public function testLogInSkipsWaitWhenLoginWaitIsZero(): void { $submit = $this->createMock(NodeElement::class); @@ -712,6 +680,38 @@ public function testLogoutConfirmUsesConfiguredUrls(): void { $this->assertFalse($user_registry->getCurrentUser()); } + protected function createSessionMock(?DocumentElement $page = NULL): Session { + $session = $this->createMock(Session::class); + $session->method('getPage')->willReturn($page ?? $this->createMock(DocumentElement::class)); + $session->method('getDriver')->willReturn($this->createMock(DriverInterface::class)); + return $session; + } + + /** + * Creates a mock for the AuthenticationCapability and BackendInterface. + * + * @return \DrevOps\BehatSteps\Backend\Capability\AuthenticationCapabilityInterface&\DrevOps\BehatSteps\Backend\BackendInterface&\PHPUnit\Framework\MockObject\MockObject + * The mocked backend. + */ + protected function createAuthBackendMock(): AuthenticationCapabilityInterface&BackendInterface&MockObject { + /** @var \DrevOps\BehatSteps\Backend\Capability\AuthenticationCapabilityInterface&\DrevOps\BehatSteps\Backend\BackendInterface&\PHPUnit\Framework\MockObject\MockObject $backend */ + $backend = $this->createMockForIntersectionOfInterfaces([ + AuthenticationCapabilityInterface::class, + BackendInterface::class, + ]); + $backend->method('isBootstrapped')->willReturn(TRUE); + return $backend; + } + + protected function createBackendRegistryMock(): BackendRegistryInterface { + $backend = $this->createMock(BackendInterface::class); + $backend->method('isBootstrapped')->willReturn(TRUE); + $backend_registry = $this->createMock(BackendRegistryInterface::class); + $backend_registry->method('hasCapability')->willReturn(FALSE); + $backend_registry->method('getBackend')->willReturn($backend); + return $backend_registry; + } + /** * Creates a Authenticator with optional overrides. * From b6b8d9835b3fca6ded25abd5d222d84e31102d18 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 13:58:12 +1000 Subject: [PATCH 14/38] Rewrote 430 source comments as plain technical statements and deleted 263 that restated the code, regenerating 'STEPS.md' and 'HELPERS.md'. --- HELPERS.md | 8 +- STEPS.md | 102 ++++++----- behat.dist.php | 4 +- behat.php | 8 +- docs.php | 18 +- rector.php | 14 +- scripts/lint-layers.php | 19 +- scripts/lint-traits.php | 7 +- scripts/merge-coverage.php | 10 +- scripts/provision.php | 90 +++++----- src/Backend/Alias/CreationAliasInterface.php | 2 +- .../Alias/CreationAliasRegistryTrait.php | 14 +- .../Alias/PostCreateAliasInterface.php | 2 +- src/Backend/Alias/PreCreateAliasInterface.php | 10 +- src/Backend/Alias/RolesAlias.php | 4 +- src/Backend/BlackboxBackendInterface.php | 5 +- .../Capability/BlockCapabilityInterface.php | 2 +- .../Capability/ConfigCapabilityInterface.php | 2 +- .../Capability/CoreCapabilityInterface.php | 2 +- .../CreationAliasCapabilityInterface.php | 11 +- .../Capability/ModuleCapabilityInterface.php | 4 +- .../Capability/StateCapabilityInterface.php | 6 +- .../Core/Alias/VocabularyMachineNameAlias.php | 7 +- src/Backend/Core/Core.php | 26 ++- src/Backend/Core/CoreInterface.php | 14 +- src/Backend/Core/Field/AbstractHandler.php | 16 +- src/Backend/Core/Field/DateRecurHandler.php | 11 +- src/Backend/Core/Field/DefaultHandler.php | 8 +- .../Core/Field/EntityReferenceHandler.php | 12 +- .../Field/EntityReferenceRevisionsHandler.php | 4 +- .../Core/Field/FieldClassifierInterface.php | 4 +- .../Core/Field/FieldShapeClassifier.php | 4 +- .../Field/FieldShapeClassifierInterface.php | 24 ++- src/Backend/Core/Field/FileHandler.php | 13 +- .../Core/Field/Parser/EntityFieldParser.php | 52 +++--- .../Parser/EntityFieldParserInterface.php | 23 ++- .../Exception/MultipleParseException.php | 8 +- src/Backend/Core/Field/ReferenceTarget.php | 3 - src/Backend/Core/Field/SmartdateHandler.php | 4 +- src/Backend/DrupalBackend.php | 6 +- src/Backend/Drush/DrushResult.php | 8 +- src/Backend/DrushBackend.php | 42 ++--- .../CreationAliasResolutionException.php | 6 +- src/Behat/Config/ConfigSchemaReader.php | 4 +- src/Behat/Config/GroupName.php | 20 ++- src/Behat/Config/Option.php | 12 +- src/Behat/Config/TagOverrides.php | 10 +- src/Behat/Config/TraitOptionResolver.php | 12 +- .../TraitOptionResolverFactoryInterface.php | 7 +- src/Behat/Context/DrupalContext.php | 14 +- src/Behat/Context/WebContext.php | 6 +- src/Behat/Context/WebRawContext.php | 60 ++++--- src/Behat/Http/HttpClientFactoryInterface.php | 4 +- src/Behat/Http/HttpIdentity.php | 2 +- src/Behat/Listener/BackendListener.php | 5 +- src/Behat/Manager/Authenticator.php | 15 +- src/Behat/Manager/BackendRegistry.php | 2 +- .../Manager/BackendRegistryInterface.php | 22 +-- src/Behat/Manager/BasicAuthenticator.php | 10 +- .../Manager/BasicAuthenticatorInterface.php | 4 +- src/Behat/Manager/ScenarioTagRegistry.php | 4 - src/Behat/Mink/Adapter/BrowserKitAdapter.php | 4 +- src/Behat/Mink/BrowserAdapterBase.php | 2 +- src/Behat/Mink/BrowserAdapterInterface.php | 6 +- src/Behat/Mink/BrowserCapabilityResolver.php | 20 +-- .../Capability/CookieCapabilityInterface.php | 14 +- .../HttpClientCapabilityInterface.php | 4 +- .../JavascriptCapabilityInterface.php | 9 +- .../RequestHeaderCapabilityInterface.php | 2 +- src/Behat/Mink/Element/DocumentElement.php | 10 +- .../Driver/BrowserKitFactory.php | 2 +- src/Behat/MinkAwareTrait.php | 11 +- src/Behat/ParametersTrait.php | 12 +- src/Behat/Prerequisite/Prerequisite.php | 11 +- src/Behat/Selector/RegionSelector.php | 2 +- .../ServiceContainer/BehatStepsExtension.php | 21 ++- src/Behat/Tag.php | 10 +- src/Helper/Drupal/AuthTrait.php | 14 +- src/Helper/Drupal/EntityLifecycleTrait.php | 21 +-- src/Helper/Drupal/FixtureFileTrait.php | 11 +- src/Helper/Web/RequestHeadersTrait.php | 2 +- src/Helper/Web/TableTransposeTrait.php | 10 +- src/Steps/Drupal/BatchTrait.php | 2 +- src/Steps/Drupal/BigPipeTrait.php | 21 ++- src/Steps/Drupal/ConfigOverrideTrait.php | 5 +- src/Steps/Drupal/DrushTrait.php | 14 +- src/Steps/Drupal/EmailTrait.php | 19 +- src/Steps/Drupal/EntityTrait.php | 2 +- src/Steps/Drupal/LanguageTrait.php | 9 +- src/Steps/Drupal/RedirectTrait.php | 12 +- src/Steps/Drupal/TaxonomyTrait.php | 4 +- src/Steps/Drupal/UserTrait.php | 8 +- src/Steps/Drupal/WatchdogTrait.php | 8 +- src/Steps/Web/AccessibilityTrait.php | 166 +++++++++--------- src/Steps/Web/CommandTrait.php | 3 +- src/Steps/Web/CookieTrait.php | 4 +- src/Steps/Web/DateTrait.php | 16 +- src/Steps/Web/DiagnosticsTrait.php | 8 +- src/Steps/Web/DropzoneTrait.php | 11 +- src/Steps/Web/ElementTrait.php | 46 ++--- src/Steps/Web/FieldTrait.php | 45 +++-- src/Steps/Web/FileDownloadTrait.php | 6 +- src/Steps/Web/IframeTrait.php | 2 - src/Steps/Web/JavascriptTrait.php | 9 +- src/Steps/Web/JsonTrait.php | 2 +- src/Steps/Web/KeyboardTrait.php | 20 +-- src/Steps/Web/LinkTrait.php | 8 +- src/Steps/Web/MappingTrait.php | 6 +- src/Steps/Web/MetatagTrait.php | 10 +- src/Steps/Web/PathTrait.php | 4 +- src/Steps/Web/RandomTrait.php | 29 ++- src/Steps/Web/ResponsiveTrait.php | 5 +- src/Steps/Web/WaitTrait.php | 7 +- src/Steps/Web/XmlTrait.php | 22 ++- tests/behat/bootstrap/BehatCliTrait.php | 12 +- tests/behat/bootstrap/FeatureContext.php | 20 +-- tests/behat/bootstrap/FeatureContextTrait.php | 12 +- tests/phpunit/src/ContextCompositionTest.php | 13 +- .../src/DataProviderConventionTest.php | 4 +- tests/phpunit/src/DocsTest.php | 37 +--- .../StepCoverage/StepCoverageTrait.php | 4 +- .../phpunit/src/Fixtures/UnwritableStream.php | 2 +- .../CoreAuthenticationMethodsKernelTest.php | 5 +- .../Core/CoreBlockMethodsKernelTest.php | 14 +- .../Core/CoreCacheMethodsKernelTest.php | 15 -- .../Core/CoreConfigMethodsKernelTest.php | 3 - .../CoreEntityCreateCommerceKernelTest.php | 8 - .../Core/CoreEntityMethodsKernelTest.php | 25 +-- .../Core/CoreMailMethodsKernelTest.php | 6 - .../Core/CoreNodeMethodsKernelTest.php | 13 +- .../Core/CoreSystemMethodsKernelTest.php | 9 - .../Core/CoreTermMethodsKernelTest.php | 16 +- .../Core/CoreUserMethodsKernelTest.php | 32 +--- ...AbstractHandlerFieldNotFoundKernelTest.php | 12 +- .../Core/Field/AddressHandlerKernelTest.php | 9 +- .../Core/Field/BooleanHandlerKernelTest.php | 6 - .../Core/Field/CustomCoreKernelTest.php | 23 +-- .../Field/CustomModuleFieldKernelTest.php | 14 +- .../Core/Field/DateRecurHandlerKernelTest.php | 9 +- .../Core/Field/DatetimeHandlerKernelTest.php | 14 +- .../Core/Field/DefaultHandlerKernelTest.php | 9 +- ...ityReferenceHandlerEdgeCasesKernelTest.php | 1 - .../EntityReferenceHandlerKernelTest.php | 25 +-- .../Core/Field/FieldHandlerKernelTestBase.php | 18 +- .../Field/FieldHandlerRegistryKernelTest.php | 22 ++- .../Field/FieldTypeCoverageKernelTest.php | 22 +-- .../Core/Field/FileHandlerKernelTest.php | 7 +- .../Core/Field/LinkHandlerKernelTest.php | 11 +- .../Core/Field/ListFloatHandlerKernelTest.php | 9 +- .../Field/ListIntegerHandlerKernelTest.php | 15 +- .../Field/ListStringHandlerKernelTest.php | 18 +- .../Core/Field/NameHandlerKernelTest.php | 23 +-- .../Core/Field/SmartdateHandlerKernelTest.php | 11 +- .../Field/SupportedImageHandlerKernelTest.php | 3 +- .../Core/Field/TimeHandlerKernelTest.php | 7 +- .../DrupalBackendConstructionKernelTest.php | 3 - ...tityLifecycleTraitVocabularyKernelTest.php | 12 -- .../Drupal/ContentBlockTraitKernelTest.php | 6 - .../Steps/Drupal/EckTraitKernelTest.php | 6 - .../Steps/Drupal/FileTraitKernelTest.php | 6 - .../Steps/Drupal/MediaTraitKernelTest.php | 6 - .../Steps/Drupal/TaxonomyTraitKernelTest.php | 6 - .../Steps/Drupal/UserTraitKernelTest.php | 9 - tests/phpunit/src/LintLayersTest.php | 16 +- tests/phpunit/src/LintTraitsTest.php | 15 -- tests/phpunit/src/MemberOrderTest.php | 6 +- tests/phpunit/src/ProvisionTest.php | 124 +------------ tests/phpunit/src/PublicSurfaceTest.php | 8 +- .../phpunit/src/StepScenarioCoverageTest.php | 14 -- tests/phpunit/src/TagReadTest.php | 2 +- tests/phpunit/src/TraitMethodNamingTest.php | 6 +- .../src/Unit/Backend/Alias/RolesAliasTest.php | 6 - .../BlackboxBackendCreationAliasesTest.php | 7 - .../src/Unit/Backend/BlackboxBackendTest.php | 25 +-- .../Backend/Core/Alias/AuthorAliasTest.php | 3 - .../Core/Alias/ParentTermAliasTest.php | 3 - .../Alias/VocabularyMachineNameAliasTest.php | 3 - .../Unit/Backend/Core/CoreErrorPathsTest.php | 14 +- .../Core/CoreFieldHandlerLookupTest.php | 29 +-- .../Backend/Core/CoreFieldMethodsTest.php | 3 - .../Unit/Backend/Core/CorePermissionsTest.php | 20 +-- .../Field/AbstractHandlerErrorPathsTest.php | 3 - .../Field/AbstractHandlerNormalizeTest.php | 9 +- .../Backend/Core/Field/AddressHandlerTest.php | 3 - .../Core/Field/EntityReferenceHandlerTest.php | 2 +- .../Core/Field/FieldClassifierTest.php | 4 +- .../Core/Field/FieldHandlerUnitTestBase.php | 6 +- .../Core/Field/FileBackedHandlerTestBase.php | 12 +- .../Backend/Core/Field/NameHandlerTest.php | 20 +-- .../src/Unit/Backend/CoreLookupTest.php | 3 - .../DrupalBackendCreationAliasesTest.php | 4 +- .../Backend/DrupalBackendDelegationTest.php | 39 +--- .../src/Unit/Backend/DrupalBackendTest.php | 19 +- .../DrushBackendCreationAliasesTest.php | 11 -- .../Unit/Backend/DrushBackendMethodsTest.php | 64 ++----- .../Unit/Backend/DrushBackendResultTest.php | 12 -- .../src/Unit/Backend/DrushBackendTest.php | 7 +- .../Unit/Backend/Entity/EntityStubTest.php | 43 ----- .../UnsupportedBackendActionExceptionTest.php | 6 - .../Fixtures/RecordingDrushBackend.php | 9 +- .../src/Unit/Behat/Config/GroupNameTest.php | 6 +- .../Unit/Behat/Config/TagOverridesTest.php | 4 +- .../Behat/Config/TraitOptionResolverTest.php | 2 +- .../Attribute/HookAttributeReaderTest.php | 5 +- .../Unit/Behat/Context/ContextConfigTest.php | 2 +- .../src/Unit/Behat/Context/WebContextTest.php | 2 +- .../Unit/Behat/Fixtures/BareMinkContext.php | 3 +- .../Fixtures/DuplicateOptionConfigContext.php | 2 +- .../Behat/Fixtures/ForeignMinkExtension.php | 2 +- .../src/Unit/Behat/Fixtures/HookedContext.php | 4 +- .../Behat/Fixtures/PrerequisiteReaderHost.php | 2 +- .../Behat/Fixtures/SampleExtraConfigTrait.php | 3 - .../Behat/Fixtures/ThrowingHookReader.php | 3 - .../src/Unit/Behat/Fixtures/UnmappedHook.php | 3 +- .../Behat/Listener/BackendListenerTest.php | 9 - .../Unit/Behat/Manager/AuthenticatorTest.php | 5 +- .../Mink/BrowserCapabilityResolverTest.php | 32 +--- .../Behat/Mink/Fixtures/AnyDriverAdapter.php | 6 +- .../Drupal/EntityLifecycleTraitTest.php | 7 +- .../Helper/Web/RequestHeadersTraitTest.php | 2 +- .../src/Unit/Helper/Web/StringTraitTest.php | 2 +- .../Unit/Steps/Drupal/WatchdogTraitTest.php | 2 +- .../Unit/Steps/Web/AccessibilityTraitTest.php | 26 ++- .../src/Unit/Steps/Web/DateTraitTest.php | 6 - .../Unit/Steps/Web/DiagnosticsTraitTest.php | 1 - .../src/Unit/Steps/Web/FieldTraitTest.php | 6 +- .../Unit/Steps/Web/FileDownloadTraitTest.php | 2 +- .../src/Unit/Steps/Web/MappingTraitTest.php | 6 - .../src/Unit/Steps/Web/MetatagTraitTest.php | 2 +- .../src/Unit/Steps/Web/ModalTraitTest.php | 6 +- .../src/Unit/Steps/Web/RandomTraitTest.php | 2 +- .../src/Unit/Steps/Web/XmlTraitTest.php | 2 +- tests/phpunit/src/UnitTestCase.php | 11 +- 233 files changed, 1096 insertions(+), 1837 deletions(-) diff --git a/HELPERS.md b/HELPERS.md index d34dd11e..8854c680 100644 --- a/HELPERS.md +++ b/HELPERS.md @@ -29,7 +29,7 @@ | [WaitTrait](#waittrait) | 1 | Wait for a period of time or for AJAX to finish. | | [XmlTrait](#xmltrait) | 6 | Assert XML responses with element and attribute checks. | | [RequestHeadersTrait](#requestheaderstrait) | 1 | Holds the request headers shared by the traits that issue HTTP requests. | -| [TableTransposeTrait](#tabletransposetrait) | 2 | Reads a vertical Gherkin table as one set of values per entity. | +| [TableTransposeTrait](#tabletransposetrait) | 2 | Reads a vertical Gherkin table as 1 set of values per entity. | ### Index of Drupal helpers @@ -180,7 +180,7 @@ Return the JavaScript source to inject into the page public function accessibilityGetPrintCli(): bool
-Return TRUE to print a one-line per-page summary to the console +Return TRUE to print a 1-line per-page summary to the console

@@ -1188,7 +1188,7 @@ Return the configured AJAX timeout, in seconds public function xmlParse(string $content): array
-Parse XML content without disturbing the cached document +Parse XML content without altering the cached document

@@ -1261,7 +1261,7 @@ $this->requestHeadersSet('X-Acme-Token', 'secret'); [Source](src/Helper/Web/TableTransposeTrait.php) -> Reads a vertical Gherkin table as one set of values per entity. +> Reads a vertical Gherkin table as 1 set of values per entity.
public function tableTransposeHorizontal(array $entities): TableNode diff --git a/STEPS.md b/STEPS.md index 06f55fab..804ceb31 100644 --- a/STEPS.md +++ b/STEPS.md @@ -88,19 +88,21 @@ > - `@behat-steps-skip:AccessibilityTrait` Opt the scenario or feature out entirely. > > Tool-agnostic. Any engine that runs inside the existing Mink session can -> be plugged in by overriding `accessibilityRunEngine()` (perform the -> assessment, return raw results) and `accessibilityNormalizeResults()` -> (remap raw output into the canonical shape the rest of the trait expects). +> be plugged in by overriding `accessibilityRunEngine()` and +> `accessibilityNormalizeResults()`. The first performs the assessment and +> returns raw results; the second remaps raw output into the canonical shape +> the rest of the trait reads. >

> Reporting. Each scenario writes its own HTML and JUnit report. After the > whole suite, a single cross-page `accessibility_report_.html` > (timestamp `YYYYMMDD_HHMMSS`) is written to the same directory, -> de-duplicating every assessed page and rolling violations up by rule. One -> file is written per run, so a run never overwrites a previous one. The +> de-duplicating every assessed page and rolling violations up by rule. +>

+> 1 file is written per run, so a run never overwrites a previous one. The > aggregate accumulates in process-global state, so under parallel Behat each > process writes its own report. >

-> Console output. A one-line per-page summary can be printed to the console +> Console output. A 1-line per-page summary can be printed to the console > as pages are assessed. Printing is off by default; set the > `BEHAT_ACCESSIBILITY_PRINT` environment variable to a non-empty value other > than `0`, or override `accessibilityGetPrintCli()`, to enable it. @@ -181,7 +183,8 @@ Then the current page should pass accessibility checks for the tags "wcag2a" > > Commands run through the system shell with the privileges of the process > that runs the tests. The command string is passed to the shell verbatim and -> is subject to shell expansion, so never interpolate untrusted input into it. +> is subject to shell expansion, so untrusted input must never be +> interpolated into it. ### Options @@ -538,10 +541,12 @@ Then a cookie with a name containing "user" and a value containing "guest" shoul > - `[relative:-1 day]` converted to `1893456000` > - `[relative:-1 day#Y-m-d]` converted to `2017-11-5` > -> `dateRelativeProcessValue()` is public API. It and its helpers are static so -> a token resolves without a context instance. Late static binding routes the -> resolution through a `dateGetNow()` override in the composing context, -> which is the supported seam for pinning the clock. +> `dateRelativeProcessValue()` is public API. It and its helpers are static, +> so a token resolves without a context instance. +>

+> Late static binding routes the resolution through a `dateGetNow()` override +> in the composing context. That override is the supported way to hold the +> current time constant. >

> Skip processing with tag: `@behat-steps-skip:DateTrait`. @@ -558,9 +563,9 @@ Then a cookie with a name containing "user" and a value containing "guest" shoul > Append on-failure diagnostics to the failure message of any failed step. >

-> When a step fails, the exception message alone is often not enough to -> diagnose a red CI run. This trait hooks every step and, only when the step -> failed, appends a compact diagnostics block to the failure message: +> The exception message of a failed step is often not enough to diagnose a +> CI failure. This trait hooks every step and, only when the step failed, +> appends a compact diagnostics block to the failure message: > - `URL` - the current page URL. > - `HTTP status` - the last response status code. > - `Browser driver` - the class of the browser driver behind the session. @@ -608,15 +613,16 @@ Then a cookie with a name containing "user" and a value containing "guest" shoul [Source](src/Steps/Web/DropzoneTrait.php), [Example](tests/behat/features/dropzone.feature) > Simulate a real multi-file drag-and-drop gesture onto a Dropzone target. -> - Drop one or more files on a CSS-selected target in a single native event. +> - Drop 1 or more files on a CSS-selected target in a single native event. > - Fixture paths resolve against the Mink `files_path` parameter. > - Works on any element that handles native `drop` events (Dropzone.js, > custom drop targets, framework widgets). >

> Mink's `attachFile` writes each file to a hidden `` > sequentially, so file A finishes uploading before file B starts. Real users -> release multiple files together, which fires a single `drop` event whose -> `dataTransfer.files` contains all of them and triggers concurrent uploads. +> release multiple files together, so a single `drop` event carries all of +> them in `dataTransfer.files` and triggers concurrent uploads. +>

> Race conditions in dedup maps, status indicators, error handlers and > server-side queues reproduce only under the multi-file path. >

@@ -641,7 +647,7 @@ When I drop the file "document.pdf" on the dropzone ".dropzone" @When I drop the following files on the dropzone :selector:
-Drop one or more files on the target element in a single native event +Drop 1 or more files on the target element in a single native event

```gherkin @@ -758,7 +764,7 @@ When I press the button "Delete" with the index 2 @When I trigger the JS event :event on the element :selector
-When I trigger the JS event :event on the element :selector +Trigger a JS event on the element defined by the selector

```gherkin @@ -1040,7 +1046,7 @@ Then the element "#page-header" should stack below the element "#modal" @Then the element :selector should be at the top of the viewport
-Assert the element :selector should be at the top of the viewport +Assert that the element is at the top of the viewport

```gherkin @@ -1054,7 +1060,7 @@ Then the element "#header" should be at the top of the viewport @Then the element :selector should be centered in the viewport
-Assert the element :selector should be centered in the viewport +Assert that the element is centered in the viewport

```gherkin @@ -1271,7 +1277,7 @@ Then the element "#main-nav" should contain 3 elements matching ".menu-item" > - Assert field existence, state, and selected options. > - Support for specialized widgets like color pickers and rich text editors. > - Disable browser validation for forms with deferred execution. -> - Use @disable-form-validation tag to automatically disable validation for all forms. +> - The @disable-form-validation tag disables validation for all forms. > > Skip processing with tag: `@behat-steps-skip:FieldTrait` @@ -2455,7 +2461,7 @@ Then the link "Return to site content" should not be an absolute link > declared in does not take part in the lookup. >

> The transform matches the token's braces rather than a placeholder name, so -> one map covers every string argument without the step opting in. +> 1 map covers every string argument without the step opting in. >

> Operates on Gherkin text alone: no Mink session and no backend, so the trait > works in any suite. @@ -3212,11 +3218,11 @@ Then the current URL should not have the query parameter "filter" with the value > Replace random-value tokens in step arguments and table cells. > - Resolve `[?:[,]]` tokens to generated values. -> - Return one value per token for the whole scenario. +> - Return 1 value per token for the whole scenario. > > Built-in types are `string`, `name`, `machine_name`, `int`, `email` and > `uuid`. The default is `string` with length `10`, so `[?title]`, -> `[?title:string]` and `[?title:string,10]` share one value. +> `[?title:string]` and `[?title:string,10]` share 1 value. >

> Operates on Gherkin text alone: no Mink session and no backend, so the trait > works in any suite. @@ -4545,7 +4551,7 @@ Then the response should be a valid Atom feed [Source](src/Steps/Drupal/BatchTrait.php), [Example](tests/behat/features/drupal_batch.feature) > Wait for Drupal's Batch API to finish. -> - Poll the batch progress element until it leaves the page. +> - Poll the batch progress element until the page no longer contains it. > > A batch page reloads itself until the operation completes, so a following > assertion would otherwise read the progress screen rather than the result. @@ -4577,18 +4583,19 @@ When I wait for the batch job to finish > replacements complete fails intermittently with "element not found". >

> With this trait included, every `@javascript` scenario waits before each -> step until no BigPipe placeholder marker remains in the DOM, which removes -> that race without an explicit step. +> step until no BigPipe placeholder marker remains in the DOM. The wait +> removes the race without an explicit step. >

> The wait is best-effort: on timeout the step still runs, so a placeholder > that is never replaced fails the following assertion rather than the wait. >

> A browser driver that runs no JavaScript never replaces those placeholders, > and does not follow the `http-equiv=refresh` fallback either. An -> authenticated-user assertion on such a browser driver silently misses -> whatever BigPipe deferred. A scenario tagged `@bigpipe` gets the -> `big_pipe_nojs` cookie, which makes Drupal render the page in full -> server-side. +> authenticated-user assertion on such a browser driver silently misses the +> content BigPipe deferred. +>

+> A scenario tagged `@bigpipe` gets the `big_pipe_nojs` cookie, so Drupal +> renders the page in full server-side. >

> Skip processing with tag: `@behat-steps-skip:BigPipeTrait`. >

@@ -4596,7 +4603,7 @@ When I wait for the batch job to finish > - `@bigpipe` - render server-side on a browser driver without JavaScript. > > Set the `big_pipe.wait_timeout` option to change the maximum wait, or assign -> `$bigPipeWaitTimeout` to override it for one scenario. +> `$bigPipeWaitTimeout` to override it for 1 scenario. ### Options @@ -5500,9 +5507,9 @@ When I save the draggable views items of the view "draggableviews_demo" and the > - Run a command that is expected to fail and keep its output. > - Assert the last command's output by substring or regular expression. > -> Steps resolve the backend that can run Drush commands rather than the one at -> the front of the scenario's order, so they work in a scenario driven by any -> other backend as long as the suite lists a Drush-capable one. +> Steps resolve the backend that can run Drush commands, not the first one in +> the scenario's order. They work in a scenario driven by any other backend +> as long as the suite lists a Drush-capable one.
@@ -6115,7 +6122,7 @@ Then the file "report.xlsx" should be attached to the email with a subject conta > created here are removed after the scenario along with every other entity > the scenario created. >

-> Skip cleanup for one type with tag: +> Skip cleanup for 1 type with tag: > `@behat-steps-entity-cleanup-skip:commerce_product`. @@ -6284,10 +6291,11 @@ Then an unmanaged file at the URI "public://config.txt" should not contain "debu > - Add languages by their ISO code, skipping ones already installed. > > Languages created here are removed after the scenario along with every other -> entity the scenario created. A scenario that also installs the 'language' -> module leaves that removal to the module uninstall, with -> '@behat-steps-entity-cleanup-skip:language', because the two teardown hooks -> run in no guaranteed order. +> entity the scenario created. +>

+> The 2 teardown hooks run in no guaranteed order. A scenario that also +> installs the 'language' module therefore leaves that removal to the module +> uninstall, with '@behat-steps-entity-cleanup-skip:language'.
@@ -6866,7 +6874,7 @@ Then the queue "myqueue" should be empty [Source](src/Steps/Drupal/RedirectTrait.php), [Example](tests/behat/features/drupal_redirect.feature) > Manage Drupal redirect entities provided by the contrib `redirect` module. -> - Create one or more redirects from a table of source/destination/status. +> - Create 1 or more redirects from a table of source/destination/status. > - Delete redirects by source path. > - Assert that redirects do or do not exist for given source paths. > - Created redirects are automatically removed at the end of the scenario. @@ -6883,7 +6891,7 @@ Then the queue "myqueue" should be empty @Given the following redirects exist:
-Create one or more redirects +Create 1 or more redirects

```gherkin @@ -6917,7 +6925,7 @@ Given the following redirects do not exist: @Then the following redirects should exist:
-Assert that one or more redirects exist +Assert that 1 or more redirects exist

```gherkin @@ -6935,7 +6943,7 @@ Then the following redirects should exist: @Then the following redirects should not exist:
-Assert that no redirect exists for one or more source paths +Assert that no redirect exists for 1 or more source paths

```gherkin @@ -7120,7 +7128,7 @@ Then the state "my_module.launched" should not exist > Manage Drupal taxonomy terms with vocabulary organization. > - Create term vocabulary structures using field values. -> - Navigate to term pages +> - Navigate to term pages. > - Verify vocabulary configurations. @@ -7246,7 +7254,7 @@ Then the vocabulary "topics" should not exist @Then the taxonomy term :term_name from the vocabulary :vocabulary should exist
-Assert that a taxonomy term exist by name +Assert that a taxonomy term exists by name

```gherkin diff --git a/behat.dist.php b/behat.dist.php index 2f45907b..08ea0f4a 100644 --- a/behat.dist.php +++ b/behat.dist.php @@ -40,8 +40,8 @@ ], ])) ->withExtension(new Extension(BehatStepsExtension::class, [ - // Both the allow-list and the precedence order: a step resolves the first - // backend here that provides the capability the step needs. + // The list is both the allow-list and the precedence order: a step + // resolves the first backend here that provides the capability it needs. 'backends' => ['drupal', 'drush', 'blackbox'], 'login_field' => 'name', 'login_wait' => 0, diff --git a/behat.php b/behat.php index 98ca0348..6ac27c32 100644 --- a/behat.php +++ b/behat.php @@ -32,7 +32,7 @@ ]); $default = (new Profile('default', ['autoload' => ['%paths.base%/tests/behat/bootstrap']])) - // Disable the Gherkin cache during development. + // The Gherkin cache is disabled during development. ->withGherkinOptions((new GherkinOptions(['cache' => '']))->withFilter(new TagFilter('~@skipped'))) ->withSuite($suite) ->withExtension(new Extension(MinkExtension::class, [ @@ -95,10 +95,10 @@ ])); } -// Drives headless Chrome directly over the DevTools Protocol, with no Selenium -// server. Run with "behat -p chrome_headless". It inherits the "default" +// The "chrome_headless" profile drives headless Chrome directly over the +// DevTools Protocol, with no Selenium server. It inherits the "default" // profile and swaps only the JavaScript session to "chrome", a session on the -// driver that ChromeExtension registers. +// driver that ChromeExtension registers. Run with "behat -p chrome_headless". $chrome_headless = (new Profile('chrome_headless')) ->withExtension(new Extension(ChromeExtension::class)) ->withExtension(new Extension(MinkExtension::class, ['javascript_session' => 'chrome', 'sessions' => ['chrome' => ['chrome' => ['api_url' => 'http://chrome_headless:9222']]]])); diff --git a/docs.php b/docs.php index 15f60e96..a6b23191 100644 --- a/docs.php +++ b/docs.php @@ -264,8 +264,8 @@ function collect_step_traits(array $class_names, array $exclude = [], string $ba sort($traits_files); } - // The scan above is consumed as each trait is found, so the membership test - // below reads a copy taken before that. + // Entries are removed from $traits_files as each trait is found, so the + // membership test below reads a copy taken first. $vocabulary = $traits_files; $collected = []; @@ -523,7 +523,7 @@ function parse_class_comment(string $trait_name, string $comment): array { $comment = preg_replace('#^/\*\*|^\s*\*\/$#m', '', $comment); $lines = explode(PHP_EOL, (string) $comment); - // Strips the docblock asterisk and at most one space, so any further + // Strips the docblock asterisk and at most 1 space, so any further // indentation is preserved. $lines = array_map(static fn(string $line): string => preg_replace('/^\s*\* ?/', '', $line), $lines); @@ -534,7 +534,6 @@ function parse_class_comment(string $trait_name, string $comment): array { array_pop($lines); } - // Lines are trimmed except inside @code blocks, where indentation is kept. $in_code_block = FALSE; $lines = array_map(static function (string $line) use (&$in_code_block): string { if (str_starts_with(trim($line), '@code')) { @@ -651,8 +650,6 @@ function parse_method_comment(string $comment): ?array { $return['description'] = trim($return['description']); if (!empty($return['example'])) { - // Indentation is removed from the example, with the first line as the - // reference. $lines = explode(PHP_EOL, $return['example']); $first_line = ''; foreach ($lines as $line) { @@ -736,7 +733,7 @@ function method_is_registered(\ReflectionMethod $method): bool { } /** - * Check whether a docblock withdraws the member from the published API. + * Check whether a docblock excludes the member from the published API. * * @param string $comment * The docblock comment. @@ -1050,7 +1047,7 @@ function extract_helpers(array $class_names, array $exclude = [], string $base_p } /** - * List the contracts a helper trait answers to. + * List the contracts the classes composing a helper trait declare. * * A trait cannot implement an interface, so the class composing it declares * the contract instead. A trait method carrying '{@inheritdoc}' is documented @@ -1114,8 +1111,8 @@ function composes_trait(\ReflectionClass $reflection, string $trait_name): bool * Collect the toolbox methods a class or trait contributes. * * Visibility is the marker: a public method that Behat does not register is - * the toolbox, and a protected one is an implementation detail carrying no - * promise to a consuming project. + * part of the toolbox. A protected method is an implementation detail with + * no contract to a consuming project. * * @param \ReflectionClass $reflection * The class or trait reflection. @@ -1702,7 +1699,6 @@ function validate_step_patterns(array $info): array { } } - // Every documented step text is collected once, with the step that owns it. $examples = []; foreach ($steps as $step) { foreach ($step['examples'] as $example) { diff --git a/rector.php b/rector.php index 19b92034..f51dea519 100644 --- a/rector.php +++ b/rector.php @@ -44,7 +44,6 @@ '/app/tests/phpunit/src', ]) ->withSkip([ - // Specific rules to skip based on project coding standards. CatchExceptionNameMatchingTypeRector::class, ChangeSwitchToMatchRector::class, InlineArrayReturnAssignRector::class, @@ -58,23 +57,19 @@ RemoveAlwaysTrueIfConditionRector::class, RenameForeachValueVariableToMatchExprVariableRector::class, // Renames a loop value after the call's return type, which names a row - // after the statement it came from and reads as camelCase where the - // coding standard wants snake_case. + // after the statement it came from. The result is camelCase where the + // coding standard requires snake_case. RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, RenameParamToMatchTypeRector::class, RenameVariableToMatchMethodCallReturnTypeRector::class, RenameVariableToMatchNewTypeRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, - // Directories to skip. '*/vendor/*', '*/node_modules/*', __DIR__ . '/tests/behat/bootstrap/BehatCliContext.php', ]) - // PHP version upgrade sets - modernizes syntax to PHP 8.3. - // Includes all rules from PHP 5.3 through 8.3. ->withPhpSets(php83: TRUE) ->withAttributesSets(behat: TRUE) - // Code quality improvement sets. ->withPreparedSets( codeQuality: TRUE, codingStyle: TRUE, @@ -83,12 +78,11 @@ privatization: TRUE, typeDeclarations: TRUE, ) - // Additional rules. ->withRules([ DeclareStrictTypesRector::class, ]) // The fixture site owns the Drupal classes the analysed code references, so - // its autoloader has to be loaded rather than only scanned: resolving a + // its autoloader has to be loaded rather than only scanned. Resolving a // parent class such as 'KernelTestBase' needs the class, not its file. ->withBootstrapFiles([ '/app/build/web/autoload.php', @@ -101,7 +95,6 @@ '/app/build/web/themes', '/app/build/web/profiles', ]) - // Drupal file extensions. ->withFileExtensions([ 'php', 'module', @@ -111,5 +104,4 @@ 'inc', 'engine', ]) - // Import configuration. ->withImportNames(importNames: FALSE, importDocBlockNames: FALSE); diff --git a/scripts/lint-layers.php b/scripts/lint-layers.php index 4efe4ad0..bc601b40 100644 --- a/scripts/lint-layers.php +++ b/scripts/lint-layers.php @@ -4,10 +4,12 @@ * @file * Layer dependency check. * - * Two layers are guaranteed to run without a dependency loaded, and the - * guarantee holds only while the layer references nothing from the - * namespaces it excludes. This script reads every file of each layer and - * fails on any code reference into those namespaces. + * 2 layers are guaranteed to run without a dependency loaded. The guarantee + * holds only while the layer references nothing from the namespaces it + * excludes. + * + * This script reads every file of each layer and fails on any code reference + * into those namespaces. * * Run with --path=path/to/repo to check a tree other than this repository. */ @@ -173,10 +175,11 @@ function layer_file_violations(string $file, array $forbidden_roots, array $allo * Reads the symbol a token refers to, if it refers to one. * * Qualified names cover imports, type declarations and inline references. A - * single-quoted literal shaped like a qualified name is included too, because - * a class name reached through a string skips the compiler but not the - * autoloader. Comments and docblocks are not code, so a prose mention of - * Behat is not a reference. + * single-quoted literal shaped like a qualified name is included too: the + * compiler does not resolve it, but the autoloader does. + * + * Comments and docblocks are not code, so a prose mention of Behat is not a + * reference. * * @param int $type * The token type. diff --git a/scripts/lint-traits.php b/scripts/lint-traits.php index d41e3561..abebcfb5 100644 --- a/scripts/lint-traits.php +++ b/scripts/lint-traits.php @@ -6,9 +6,10 @@ * * A trait's directory determines its kind: 'src/Steps' holds the step * vocabulary and 'src/Helper' holds the plumbing shared by step traits. Each - * is split into a 'Web' and a 'Drupal' half. This script reads both trees and - * fails when a step trait composes another step trait, or when a helper trait - * registers Gherkin. + * is split into a 'Web' and a 'Drupal' half. + * + * This script reads both trees and fails when a step trait composes another + * step trait, or when a helper trait registers Gherkin. * * A helper may register a hook: the trait that owns a teardown carries the * hook that runs it. diff --git a/scripts/merge-coverage.php b/scripts/merge-coverage.php index 26a39753..73a749c4 100644 --- a/scripts/merge-coverage.php +++ b/scripts/merge-coverage.php @@ -10,8 +10,8 @@ * Where coverage_root_path is the optional path to the coverage root directory. * Defaults to '/app/.logs/coverage'. * - * This will also generate Cobertura and HTML reports from the merged coverage - * data. + * The script also generates Cobertura and HTML reports from the merged + * coverage data. */ declare(strict_types=1); @@ -20,9 +20,9 @@ use SebastianBergmann\CodeCoverage\Report\Cobertura; use SebastianBergmann\CodeCoverage\Report\Html\Facade; -// The coverage files are serialised by the fixture site's php-code-coverage, -// and only unserialise against that same installation, so the fixture's -// autoloader is preferred over the project's own. +// The fixture site's php-code-coverage serialised the coverage files, and +// they unserialise only against that installation. The fixture's autoloader +// is therefore preferred over the project's own. $autoloader = __DIR__ . '/../build/vendor/autoload.php'; if (!file_exists($autoloader)) { $autoloader = __DIR__ . '/../vendor/autoload.php'; diff --git a/scripts/provision.php b/scripts/provision.php index 1e339980..5b3e668f 100644 --- a/scripts/provision.php +++ b/scripts/provision.php @@ -55,8 +55,8 @@ /** * The Drupal major the fixture installs from prerelease. * - * No contrib release declares it, so the solve needs relaxing before it - * resolves, and its "drupal/core-dev" is the first to bring PHPUnit 12. + * No contrib release declares it, so the solve resolves only once relaxed. + * Its "drupal/core-dev" is also the first to require PHPUnit 12. */ const PROVISION_DRUPAL_NEXT_MAJOR = 12; @@ -82,9 +82,11 @@ * Drupal's own test namespaces, and the docroot path each maps to. * * Drupal registers them from its PHPUnit bootstrap rather than from a - * Composer entry, so a tool that loads only the autoloader cannot resolve a - * class such as KernelTestBase. The list is maintained by hand: the merge - * runs before the install, so the docroot cannot be scanned for it. + * Composer entry. A tool that loads only the autoloader therefore cannot + * resolve a class such as KernelTestBase. + * + * The list is maintained by hand: the merge runs before the install, so the + * docroot cannot be scanned for it. */ const PROVISION_DRUPAL_TEST_NAMESPACES = [ 'BuildTests', @@ -105,8 +107,8 @@ * A content type only the fixture defines. * * "drush cim" can enable the modules, abort on a fatal raised while it - * creates config entities, and still exit 0, so the import is confirmed - * against an entity that core does not ship. + * creates config entities, and still exit 0. The import is therefore + * confirmed against an entity that core does not ship. */ const PROVISION_FIXTURE_CONTENT_TYPE = 'landing_page'; @@ -198,10 +200,10 @@ function provision(): void { } if (provision_is_lenient($drupal_version)) { - // A plugin only shapes a solve that it is already installed for, and the - // fixture has no solution until this one relaxes the contrib core - // constraints. Composer loads globally installed plugins for local - // projects, so installing it outside the build breaks that circle. + // A plugin takes part in a solve only once installed, and the fixture has + // no solution until this one relaxes the contrib core constraints. + // Composer loads globally installed plugins for local projects, so a + // global install breaks the circular dependency. echo ' > Installing the Composer plugin that relaxes contrib core constraints.' . PHP_EOL; $constraint = provision_lenient_constraint($fixture_dir . '/composer.json'); provision_run('composer global config --no-interaction allow-plugins.' . PROVISION_LENIENT_PLUGIN . ' true'); @@ -259,14 +261,15 @@ function provision(): void { /** * Applies the package's own patches through Composer Patches. * - * A patch declared in composer.json is read by the Composer Patches - * "Dependencies" resolver in every project that requires this package, which - * resolves the path against that project's own root. The declaration is - * written here, over the package's own composer.json, and reverted once - * Composer has applied it. + * The Composer Patches "Dependencies" resolver reads a patch declared in + * composer.json in every project that requires this package. It resolves the + * path against that project's own root. + * + * The declaration is therefore written here, over the package's own + * composer.json, and reverted once Composer has applied it. * * The patches apply to this package's own vendor directory, not to the - * fixture site: "ahoy lint" runs the root vendor/bin/phpstan, and + * fixture site. "ahoy lint" runs the root vendor/bin/phpstan, and * mglaman/phpstan-drupal reads DRUPAL_ROOT and DRUPAL_VENDOR_ROOT only once * patched. * @@ -284,9 +287,8 @@ function provision_apply_patches(): void { echo " > Applying the package's own patches through Composer Patches." . PHP_EOL; - // The bytes restored in the finally are the bytes that were decoded, so a - // read that returned nothing cannot reach the restore and truncate the - // package's own manifest. + // The finally writes $original back, so a read that returned FALSE is + // rejected here rather than written over the package's own manifest. $composer_file = PROVISION_PACKAGE_ROOT . '/composer.json'; $original = file_get_contents($composer_file); @@ -304,9 +306,9 @@ function provision_apply_patches(): void { echo sprintf(' %s: %d patch(es)%s', $package, count($entries), PHP_EOL); } - // "install" applies a patch only while it installs the package the patch - // belongs to, and the dependencies are in place by now, so the packages - // carrying one are re-fetched and re-patched explicitly. + // "install" applies a patch only while installing the patched package, + // and the dependencies are already in place. The patched packages are + // therefore re-fetched and re-patched explicitly. $composer = 'composer --working-dir=' . PROVISION_PACKAGE_ROOT . ' --ansi --no-interaction '; provision_run($composer . 'patches-relock', ['COMPOSER_MEMORY_LIMIT' => '-1']); provision_run($composer . 'patches-repatch', ['COMPOSER_MEMORY_LIMIT' => '-1']); @@ -321,9 +323,9 @@ function provision_apply_patches(): void { /** * Maps the patch files under a directory to the packages they apply to. * - * A patch file lives at "patches///.patch", so the - * package it applies to is its own directory and the set is iterated rather - * than listed. + * A patch file sits at "patches///.patch", so its + * directory names the package it applies to. The set is iterated rather than + * listed. * * @param string $directory * Absolute path to the directory holding the patches. @@ -468,10 +470,10 @@ function provision_write_auth(string $token, string $file): void { return; } - // The file holds a credential. A umask denies every other user from the - // moment the file is created, where a chmod() after the write would leave - // the token readable in between. The build directory was emptied above, so - // the file cannot already exist with a mode of its own. + // A umask denies every other user from the moment the file is created, + // where a chmod() after the write would leave the token readable in + // between. The build directory was emptied above, so the file cannot + // already exist with a mode of its own. $umask = umask(0077); try { @@ -483,7 +485,7 @@ function provision_write_auth(string $token, string $file): void { } /** - * Writes a file, reporting a write that did not land. + * Writes a file, reporting a write that failed. * * Every write the script makes goes through here. * @@ -636,9 +638,9 @@ function provision_widen_contrib(string $directory, string $major): int { * Admits a Drupal major into an extension's core version requirement. * * Composer installs the contrib code, but Drupal reads - * core_version_requirement from each extension and refuses to enable one - * that excludes the running major. No contrib release declares Drupal 12, so - * the fixture widens what it received. + * core_version_requirement from each extension and does not enable one that + * excludes the running major. No contrib release declares Drupal 12, so the + * fixture widens what it received. * * @param string $text * The contents of an info file. @@ -669,8 +671,8 @@ function provision_widen_core_requirement(string $text, string $major): string { /** * Appends the fixture config overrides to a settings file. * - * Drupal leaves the installed settings.php read-only, so the write is opened - * and closed around. + * Drupal leaves the installed settings.php read-only, so the file is made + * writable for the write and read-only again after it. * * @param string $file * Absolute path to the settings file. @@ -769,10 +771,10 @@ function provision_write_merged_composer(string $package_file, string $fixture_f /** * Merges the package's Composer configuration into the fixture's. * - * The fixture site exercises every trait at once, so what a consumer opts - * into package by package is all required here: the package's own runtime - * requirements, the "require-dev" entries that back a "suggest" entry, and - * the packages that run the test suites. + * The fixture site exercises every trait at once, so everything a consumer + * opts into package by package is required here. This covers the package's + * own runtime requirements, the "require-dev" entries that back a "suggest" + * entry, and the packages that run the test suites. * * @param array $package * Decoded contents of the package's composer.json. @@ -808,9 +810,9 @@ function provision_merge_composer(array $package, array $fixture): array { $merged = array_replace_recursive($filtered, $fixture); - // A package named in both sections resolves to the lower of the 2 - // constraints under "--prefer-lowest", which can fall outside the range the - // fixture pins, so the fixture constraint is the one that survives. + // Under "--prefer-lowest", a package in both sections resolves to the lower + // constraint, which can fall outside the range the fixture pins. The + // fixture constraint is therefore kept. $merged['require-dev'] = array_diff_key(provision_section($merged, 'require-dev'), provision_section($merged, 'require')); return $merged; @@ -819,7 +821,7 @@ function provision_merge_composer(array $package, array $fixture): array { /** * Prefixes every PSR-4 path of an autoload section with "../". * - * The build sits one level below the package root, so a path the package + * The build sits 1 level below the package root, so a path the package * declares relative to itself only resolves from the build with the prefix. * A namespace may map to a list of directories rather than to one. * diff --git a/src/Backend/Alias/CreationAliasInterface.php b/src/Backend/Alias/CreationAliasInterface.php index ecd6a241..f34212a7 100644 --- a/src/Backend/Alias/CreationAliasInterface.php +++ b/src/Backend/Alias/CreationAliasInterface.php @@ -7,7 +7,7 @@ /** * Base contract for a creation alias. * - * An alias represents one ergonomic stub property that is not a real + * An alias represents 1 ergonomic stub property that is not a real * Drupal field, together with the resolution behaviour the backend * applies during entity creation. Concrete aliases implement either * 'PreCreateAliasInterface' (to mutate the stub before save) or diff --git a/src/Backend/Alias/CreationAliasRegistryTrait.php b/src/Backend/Alias/CreationAliasRegistryTrait.php index c9068543..39812e51 100644 --- a/src/Backend/Alias/CreationAliasRegistryTrait.php +++ b/src/Backend/Alias/CreationAliasRegistryTrait.php @@ -10,12 +10,12 @@ * Implements the 'CreationAliasCapabilityInterface' registry on a backend. * * Hosts an entity-type-indexed map of registered aliases, exposes the - * 'getCreationAliases()' lookup, and provides two protected dispatcher - * helpers for create methods to invoke: 'applyPreCreateAliases()' before - * 'Entity::create()' and 'applyPostCreateAliases()' after save. + * 'getCreationAliases()' lookup, and provides 2 protected dispatchers for + * create methods: 'applyPreCreateAliases()' before 'Entity::create()' and + * 'applyPostCreateAliases()' after save. * - * Both dispatchers gate on 'EntityStubInterface::hasValue()' so an alias - * only fires when the stub actually carries the key it owns. + * Both dispatchers gate on 'EntityStubInterface::hasValue()', so an alias + * runs only when the stub carries the key it owns. */ trait CreationAliasRegistryTrait { @@ -30,8 +30,8 @@ trait CreationAliasRegistryTrait { * Adds an alias to the registry. * * Re-registering the same name on the same entity type replaces the - * previous entry so subclasses can override aliases by simply - * registering a new instance after 'parent::' setup. + * previous entry, so a subclass overrides an alias by registering a new + * instance after 'parent::' setup. * * @param \DrevOps\BehatSteps\Backend\Alias\CreationAliasInterface $alias * The alias to register. diff --git a/src/Backend/Alias/PostCreateAliasInterface.php b/src/Backend/Alias/PostCreateAliasInterface.php index 1a018e64..41650431 100644 --- a/src/Backend/Alias/PostCreateAliasInterface.php +++ b/src/Backend/Alias/PostCreateAliasInterface.php @@ -9,7 +9,7 @@ /** * A creation alias that acts on the entity after it has been saved. * - * Use this lifecycle for side-effects that require the entity to exist + * This lifecycle is for side-effects that require the entity to exist * first - for example, assigning roles to a user after the user record * has been written, or attaching references to an entity that needs an * id before it can be linked. diff --git a/src/Backend/Alias/PreCreateAliasInterface.php b/src/Backend/Alias/PreCreateAliasInterface.php index 49de057f..2e3b3392 100644 --- a/src/Backend/Alias/PreCreateAliasInterface.php +++ b/src/Backend/Alias/PreCreateAliasInterface.php @@ -11,7 +11,7 @@ * * Implementations resolve the alias value, write any derived real-field * values back onto the stub, and remove the alias's own key from the - * stub so the values bag passed to Drupal's entity factory contains + * stub. The values bag passed to Drupal's entity factory then contains * only real fields. */ interface PreCreateAliasInterface extends CreationAliasInterface { @@ -19,10 +19,10 @@ interface PreCreateAliasInterface extends CreationAliasInterface { /** * Resolves the alias value and mutates the stub in place. * - * Implementations MUST remove the alias's own value from the stub - * (via 'EntityStubInterface::removeValue()') once resolution succeeds, - * unless they intentionally overwrite the same key with the resolved - * representation (e.g. swapping a term name for a tid). + * Implementations MUST remove the alias's own value from the stub via + * 'EntityStubInterface::removeValue()' once resolution succeeds. The + * exception is an alias that deliberately overwrites the same key with + * the resolved representation, such as a term name swapped for a tid. * * @param \DrevOps\BehatSteps\Backend\Entity\EntityStubInterface $stub * The stub being prepared for creation. Must already carry a value diff --git a/src/Backend/Alias/RolesAlias.php b/src/Backend/Alias/RolesAlias.php index 3387e0a6..1541ce61 100644 --- a/src/Backend/Alias/RolesAlias.php +++ b/src/Backend/Alias/RolesAlias.php @@ -60,8 +60,8 @@ public function applyAfterCreate(EntityStubInterface $stub, object $entity): voi foreach ($roles as $role) { // EntityReferenceHandler expands 'roles' into records like - // '['target_id' => 'editor']'. Unwrap that here so the alias can - // operate on the same role name the caller supplied originally. + // ['target_id' => 'editor'], so the record is unwrapped back to the + // role name the caller supplied. if (is_array($role) && array_key_exists('target_id', $role)) { $role = $role['target_id']; } diff --git a/src/Backend/BlackboxBackendInterface.php b/src/Backend/BlackboxBackendInterface.php index 1d15890c..915cb892 100644 --- a/src/Backend/BlackboxBackendInterface.php +++ b/src/Backend/BlackboxBackendInterface.php @@ -9,8 +9,9 @@ * * Performs no backend operations. Implementations satisfy only the base * backend contract and MUST NOT implement any interface in the - * 'DrevOps\BehatSteps\Backend\Capability' namespace, so 'instanceof - * BlackboxBackendInterface' is a reliable negative-capability guarantee. + * 'DrevOps\BehatSteps\Backend\Capability' namespace. An 'instanceof + * BlackboxBackendInterface' check is therefore a reliable + * negative-capability guarantee. */ interface BlackboxBackendInterface extends BackendInterface { diff --git a/src/Backend/Capability/BlockCapabilityInterface.php b/src/Backend/Capability/BlockCapabilityInterface.php index 5e18a029..648981b7 100644 --- a/src/Backend/Capability/BlockCapabilityInterface.php +++ b/src/Backend/Capability/BlockCapabilityInterface.php @@ -9,7 +9,7 @@ /** * Capability: place blocks and create content blocks. * - * Groups the two distinct block operations a backend typically needs during + * Groups the 2 distinct block operations a backend typically needs during * scenario setup: * * - Placing a block in a region - the 'block' config entity. diff --git a/src/Backend/Capability/ConfigCapabilityInterface.php b/src/Backend/Capability/ConfigCapabilityInterface.php index 702087f7..025fa45b 100644 --- a/src/Backend/Capability/ConfigCapabilityInterface.php +++ b/src/Backend/Capability/ConfigCapabilityInterface.php @@ -73,7 +73,7 @@ public function configGetData(string $name): array; * Replaces every stored value of a configuration object. * * Keys absent from the given data are dropped, so restoring a snapshot - * removes the keys a scenario added. + * removes the keys added since it was taken. * * @param string $name * The configuration object name. diff --git a/src/Backend/Capability/CoreCapabilityInterface.php b/src/Backend/Capability/CoreCapabilityInterface.php index 2d96174e..507ea0f7 100644 --- a/src/Backend/Capability/CoreCapabilityInterface.php +++ b/src/Backend/Capability/CoreCapabilityInterface.php @@ -11,7 +11,7 @@ * * A backend providing this capability has Drupal bootstrapped once it is * resolved, so '\Drupal::' statics and the entity API are reachable. A step - * that calls into Drupal directly asks for this capability rather than for a + * that calls into Drupal directly requires this capability instead of a * backend class. */ interface CoreCapabilityInterface { diff --git a/src/Backend/Capability/CreationAliasCapabilityInterface.php b/src/Backend/Capability/CreationAliasCapabilityInterface.php index f0b19da7..9602e38a 100644 --- a/src/Backend/Capability/CreationAliasCapabilityInterface.php +++ b/src/Backend/Capability/CreationAliasCapabilityInterface.php @@ -5,12 +5,13 @@ namespace DrevOps\BehatSteps\Backend\Capability; /** - * Capability: expose the entity creation aliases the backend understands. + * Capability: expose the backend's entity creation aliases. * - * Creation aliases are ergonomic property names on entity stubs that - * are not real Drupal fields - for example, 'author' on a node stub - * that the backend translates to a 'uid' value during creation. This - * capability lets consumers discover which aliases are accepted, for + * Creation aliases are convenience property names on entity stubs that are + * not real Drupal fields. An example is 'author' on a node stub, which the + * backend translates to a 'uid' value during creation. + * + * This capability lets consumers discover which aliases are accepted, for * what entity type, and how to document them. * * This interface is intentionally NOT extended by the composite backend diff --git a/src/Backend/Capability/ModuleCapabilityInterface.php b/src/Backend/Capability/ModuleCapabilityInterface.php index a38f8af0..794c5988 100644 --- a/src/Backend/Capability/ModuleCapabilityInterface.php +++ b/src/Backend/Capability/ModuleCapabilityInterface.php @@ -36,8 +36,8 @@ public function moduleIsEnabled(string $module_name): bool; /** * Whether a module's code is present, installed or not. * - * Distinguishes a module that is merely disabled from one the site cannot - * install because its code is absent. + * Distinguishes a disabled module from one the site cannot install because + * its code is absent. * * @param string $module_name * The module machine name. diff --git a/src/Backend/Capability/StateCapabilityInterface.php b/src/Backend/Capability/StateCapabilityInterface.php index 2a314b8a..59ac1511 100644 --- a/src/Backend/Capability/StateCapabilityInterface.php +++ b/src/Backend/Capability/StateCapabilityInterface.php @@ -41,9 +41,9 @@ public function stateDelete(string $name): void; /** * Whether a state key is set. * - * A key holding NULL counts as set where the backend can tell the difference. - * A backend reading state through 'stateGet()' alone cannot, and says so on - * its own implementation. + * A key holding NULL counts as set where the backend can distinguish it + * from an absent key. A backend reading state through 'stateGet()' alone + * cannot, and documents this on its own implementation. * * @param string $name * The state key. diff --git a/src/Backend/Core/Alias/VocabularyMachineNameAlias.php b/src/Backend/Core/Alias/VocabularyMachineNameAlias.php index 9e0ee5c4..fdfa20e2 100644 --- a/src/Backend/Core/Alias/VocabularyMachineNameAlias.php +++ b/src/Backend/Core/Alias/VocabularyMachineNameAlias.php @@ -12,9 +12,10 @@ * * The typed bundle constructor argument and an explicit 'vid' value * both take priority over this alias. When neither is present the - * alias value is copied to 'vid'. The alias key is always removed once - * handled. The alias does not validate vocabulary existence - the - * create method does that after all pre-create aliases have run. + * alias value is copied to 'vid'. + * + * The alias key is always removed once handled. The alias does not + * validate vocabulary existence. */ class VocabularyMachineNameAlias implements PreCreateAliasInterface { diff --git a/src/Backend/Core/Core.php b/src/Backend/Core/Core.php index 37e5599d..d546a242 100644 --- a/src/Backend/Core/Core.php +++ b/src/Backend/Core/Core.php @@ -70,11 +70,6 @@ class Core implements CoreInterface, AuthenticationCapabilityInterface, Creation /** * Registered field handler classes, keyed by field type id. * - * Populated at construction with the project's built-in handlers and - * extended at runtime via 'registerFieldHandler()'. Lookup in - * 'getFieldHandler()' consults this map first, falling back to - * 'DefaultHandler' when a field type has no registered class. - * * @var array> */ protected array $fieldHandlers = []; @@ -102,7 +97,7 @@ class Core implements CoreInterface, AuthenticationCapabilityInterface, Creation * @param string $drupal_root * The absolute path to the Drupal root directory. * @param string $uri - * URI that is accessing Drupal. Defaults to 'default'. + * The URI used to access Drupal. Defaults to 'default'. * @param \Drupal\Component\Utility\Random|null $random * Optional random-value generator. */ @@ -339,12 +334,11 @@ protected function expandEntityFields(EntityStubInterface $stub): void { $entity_type = $stub->getEntityType(); $definition = $this->loadEntityTypeDefinition($entity_type); - // The id key and bundle key identify the record itself and must not pass - // through the handler pipeline. On 'commerce_product' the bundle key - // 'type' is also a base entity_reference field. Expanding it would - // resolve the bundle machine name through EntityReferenceHandler and - // overwrite the scalar with ['target_id' => ...], corrupting every - // subsequent bundle lookup for the same stub. + // The id key and bundle key identify the record, so neither enters the + // handler pipeline. On 'commerce_product' the bundle key 'type' is also + // a base entity_reference field, and EntityReferenceHandler would + // replace the scalar with ['target_id' => ...], corrupting the stub's + // later bundle lookups. $skip = array_filter([$definition->getKey('id'), $definition->getKey('bundle')]); $bundle = $this->resolveBundle($stub); @@ -368,9 +362,9 @@ protected function expandEntityFields(EntityStubInterface $stub): void { /** * Resolves the bundle for an entity stub. * - * Consults the entity type's bundle key in the values bag first, then the - * typed 'bundle' constructor argument, then falls back to the entity type - * id (single-bundle entities like 'user' use the type id as their bundle). + * Reads the entity type's bundle key from the values bag first, then the + * typed 'bundle' constructor argument, then the entity type id. + * Single-bundle entities like 'user' use the type id as their bundle. * * @param \DrevOps\BehatSteps\Backend\Entity\EntityStubInterface $stub * The stub. @@ -1099,7 +1093,7 @@ public function stateDelete(string $name): void { */ public function stateExists(string $name): bool { // The state service reads a stored NULL and an absent key alike, so the - // backing key-value store answers this instead. + // backing key-value store is queried instead. return \Drupal::keyValue('state')->has($name); } diff --git a/src/Backend/Core/CoreInterface.php b/src/Backend/Core/CoreInterface.php index 20dc5031..1727d4b0 100644 --- a/src/Backend/Core/CoreInterface.php +++ b/src/Backend/Core/CoreInterface.php @@ -29,8 +29,8 @@ * handler, and so on) with the operational capabilities every Core provides. * * Authentication is deliberately absent: a Core declares - * 'AuthenticationCapabilityInterface' separately, so 'instanceof' answers - * whether it can log a user in. + * 'AuthenticationCapabilityInterface' separately, so an 'instanceof' check + * determines whether it can log a user in. */ interface CoreInterface extends BatchCapabilityInterface, @@ -101,11 +101,11 @@ public function getFieldHandler(EntityStubInterface $stub, string $entity_type, * Registers a field handler class for a field type. * * Overrides one of the backend's built-in handlers or adds a handler for a - * field type the backend does not ship one for. The registration wins over - * the defaults registered by 'Core::registerDefaultFieldHandlers()' in the - * constructor. Handlers must implement 'FieldHandlerInterface'; a class - * that does not triggers a 'RuntimeException' at registration - * time rather than at field resolution time. + * field type the backend does not ship one for. The registration replaces + * the default registered by 'Core::registerDefaultFieldHandlers()' in the + * constructor. A class that does not implement 'FieldHandlerInterface' + * triggers a 'RuntimeException' at registration time rather than at field + * resolution time. * * @param string $field_type * The Drupal field type id, e.g. 'boolean', 'entity_reference', or a diff --git a/src/Backend/Core/Field/AbstractHandler.php b/src/Backend/Core/Field/AbstractHandler.php index d0888f1f..b51f038e 100644 --- a/src/Backend/Core/Field/AbstractHandler.php +++ b/src/Backend/Core/Field/AbstractHandler.php @@ -54,9 +54,8 @@ public function __construct(EntityStubInterface $stub, string $entity_type, stri $entity_field_manager = \Drupal::service('entity_field.manager'); $storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type); - // Bundle precedence is bundle key value, then typed bundle, then entity - // type (single-bundle entities like 'user' use the entity type as the - // bundle). + // A single-bundle entity type such as 'user' uses the entity type as its + // bundle, so the entity type is the fallback. $bundle_key = \Drupal::entityTypeManager()->getDefinition($entity_type)->getKey('bundle'); $bundle = $entity_type; @@ -91,7 +90,7 @@ final public function expand(mixed $values): array { * Recognised input shapes: * - Bare scalar -> wrapped as a single record using the main property. * - List of scalars -> each wrapped as a record. - * - Single keyed record -> wrapped in a one-element list. + * - Single keyed record -> wrapped in a 1-element list. * - List of records -> returned unchanged. * - Mixed list of scalars and records -> scalars wrapped, records kept. * @@ -119,8 +118,8 @@ protected function normalize(mixed $values): array { } // '['foo.jpg', 'alt' => 'A']' is ambiguous: 'foo.jpg' could be the main - // value with 'alt' as an extra, or two separate deltas with one of them - // named. The mixed shape is rejected. + // value with 'alt' as an extra, or 2 separate deltas with 1 of them + // named. $has_int_key = FALSE; $has_string_key = FALSE; @@ -152,9 +151,8 @@ protected function normalize(mixed $values): array { } // A record without the main property is almost always a caller mistake: - // the path, value or uri is missing and only extras like 'alt' or - // 'format' remain. Rejecting it here stops a handler dispatching on - // missing data. + // only extras like 'alt' or 'format' remain. Rejecting it here stops a + // handler dispatching on missing data. foreach ($records as $record) { if (!array_key_exists($this->mainProperty, $record)) { throw new \RuntimeException(sprintf( diff --git a/src/Backend/Core/Field/DateRecurHandler.php b/src/Backend/Core/Field/DateRecurHandler.php index 23957bba..343c6e2d 100644 --- a/src/Backend/Core/Field/DateRecurHandler.php +++ b/src/Backend/Core/Field/DateRecurHandler.php @@ -9,10 +9,13 @@ * * The base normalize() folds the bare-scalar 'value' shorthand and keyed * multi-column records ('value', 'end_value', 'rrule', 'timezone', - * 'infinite'). The 'value' and 'end_value' columns are stored verbatim and - * interpreted in the record's own 'timezone' (not UTC), and the field type's - * preSave() derives 'infinite' from the rrule. The handler therefore relays - * the multi-column records through unchanged. + * 'infinite'). + * + * The 'value' and 'end_value' columns are stored verbatim and interpreted in + * the record's own 'timezone' (not UTC). The field type's preSave() derives + * 'infinite' from the rrule. + * + * The handler therefore relays the multi-column records unchanged. * * @see https://www.drupal.org/project/date_recur */ diff --git a/src/Backend/Core/Field/DefaultHandler.php b/src/Backend/Core/Field/DefaultHandler.php index b5578605..2b0eb6c1 100644 --- a/src/Backend/Core/Field/DefaultHandler.php +++ b/src/Backend/Core/Field/DefaultHandler.php @@ -7,10 +7,10 @@ /** * Fallback handler for field types with no dedicated handler. * - * Relays the normalised records to storage verbatim. A field this handler - * cannot marshal - an entity-reference target or a complex/nested value - is - * rejected during handler selection (see 'FieldShapeClassifierInterface'), - * so the field is a plain-scalar shape by the time this handler runs. + * Relays the normalised records to storage verbatim. An entity-reference + * target or a complex/nested value is rejected during handler selection (see + * 'FieldShapeClassifierInterface'), so every field this handler receives is + * a plain-scalar shape. * * See 'src/Backend/Core/Field/README.md' for the full handler-selection * table. diff --git a/src/Backend/Core/Field/EntityReferenceHandler.php b/src/Backend/Core/Field/EntityReferenceHandler.php index 54f6ae9e..4eec9123 100644 --- a/src/Backend/Core/Field/EntityReferenceHandler.php +++ b/src/Backend/Core/Field/EntityReferenceHandler.php @@ -23,9 +23,8 @@ protected function doExpand(array $records): array { $lookup = $record[$this->mainProperty]; - // Already-resolved integer ids (caller-supplied or alias-resolved) - // bypass the entity-storage round-trip; only string labels still - // need a lookup. + // An integer id is already resolved; only a string lookup requires an + // entity query. if (is_int($lookup)) { $resolved[] = $record; continue; @@ -87,10 +86,9 @@ protected function resolveTargetId(mixed $lookup, ReferenceTarget $target): int| $query->accessCheck(FALSE); if ($target->labelKey) { - // A numeric-string lookup is ambiguous: the caller may be passing an - // entity id that Drupal serialised as a string, or a label that - // happens to be digits. An OR-group matches either side, and the - // entity layer's first hit wins. + // A numeric-string lookup is ambiguous: an entity id Drupal serialised + // as a string, or a label made of digits. An OR-group matches either, + // and the first match is returned. $is_numeric_id = is_string($lookup) && ctype_digit($lookup); $or = $query->orConditionGroup(); diff --git a/src/Backend/Core/Field/EntityReferenceRevisionsHandler.php b/src/Backend/Core/Field/EntityReferenceRevisionsHandler.php index 94bad146..41ea4479 100644 --- a/src/Backend/Core/Field/EntityReferenceRevisionsHandler.php +++ b/src/Backend/Core/Field/EntityReferenceRevisionsHandler.php @@ -10,8 +10,8 @@ /** * Field handler for 'entity_reference_revisions' fields (Paragraphs et al). * - * A revision reference resolves its target the same way a plain entity - * reference does, then records the target's revision id beside the id. + * The handler resolves the target as 'EntityReferenceHandler' does, then + * records the target's revision id beside the id. */ class EntityReferenceRevisionsHandler extends EntityReferenceHandler { diff --git a/src/Backend/Core/Field/FieldClassifierInterface.php b/src/Backend/Core/Field/FieldClassifierInterface.php index 5ef95b30..e735242e 100644 --- a/src/Backend/Core/Field/FieldClassifierInterface.php +++ b/src/Backend/Core/Field/FieldClassifierInterface.php @@ -5,9 +5,9 @@ namespace DrevOps\BehatSteps\Backend\Core\Field; /** - * Classifies Drupal fields into the nine mutually exclusive F-row categories. + * Classifies Drupal fields into the 9 mutually exclusive F-row categories. * - * Each predicate answers "is this field in F{N}?" for one row of the truth + * Each predicate answers "is this field in F{N}?" for 1 row of the truth * table, based only on the field's declaration and storage profile. The * classifier does not decide what is done with a classification; that * decision belongs to the consumer. diff --git a/src/Backend/Core/Field/FieldShapeClassifier.php b/src/Backend/Core/Field/FieldShapeClassifier.php index 5564ef6e..7c357148 100644 --- a/src/Backend/Core/Field/FieldShapeClassifier.php +++ b/src/Backend/Core/Field/FieldShapeClassifier.php @@ -46,8 +46,8 @@ public function fieldIsComplexValue(FieldStorageDefinitionInterface $storage): b /** * Yields a field's stored (non-computed) property definitions. * - * Computed properties are storage-derived, not author-supplied, so they never - * bear on whether the caller can express the field as a plain scalar. + * Computed properties are storage-derived, not author-supplied, so they do + * not affect the field's value shape. * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage * The field storage definition to inspect. diff --git a/src/Backend/Core/Field/FieldShapeClassifierInterface.php b/src/Backend/Core/Field/FieldShapeClassifierInterface.php index c6e891c2..1d48c81c 100644 --- a/src/Backend/Core/Field/FieldShapeClassifierInterface.php +++ b/src/Backend/Core/Field/FieldShapeClassifierInterface.php @@ -11,15 +11,16 @@ * * 'FieldClassifierInterface' answers the pipeline-entry (F-row) question from * a field's origin and storage profile. This interface answers the orthogonal - * value-shape question the README calls a "handler-selection input": whether - * a field's stored value is a plain scalar the default handler can relay, or - * a shape that needs a dedicated handler. + * value-shape question the README calls a "handler-selection input". * - * Both predicates read only the storage definition's stored (non-computed) - * property definitions and enumerate no field-type or data-type strings. A - * datetime, boolean, or list column is therefore neither an entity reference - * nor complex: it is a plain scalar the default relays, and value translation - * for it belongs in a dedicated handler. + * A stored value is either a plain scalar the default handler can relay or a + * shape that requires a dedicated handler. Both predicates read only the + * storage definition's stored (non-computed) property definitions and + * enumerate no field-type or data-type strings. + * + * A datetime, boolean, or list column is therefore neither an entity + * reference nor complex: it is a plain scalar the default relays. Value + * translation for such a column belongs in a dedicated handler. * * See 'src/Backend/Core/Field/README.md' for the value-shape axis and how * 'Core' consumes it during handler selection. @@ -33,9 +34,7 @@ interface FieldShapeClassifierInterface { * The field storage definition to inspect. * * @return bool - * TRUE when a non-computed property is a 'DataReferenceTargetDefinition' - - * the caller supplies a label, path, or name a dedicated handler must - * resolve to an id the author cannot know. + * TRUE when a non-computed property is a 'DataReferenceTargetDefinition'. */ public function fieldIsEntityReference(FieldStorageDefinitionInterface $storage): bool; @@ -47,8 +46,7 @@ public function fieldIsEntityReference(FieldStorageDefinitionInterface $storage) * * @return bool * TRUE when a non-computed property is a 'ComplexDataDefinitionInterface' - * (e.g. a map) or a 'ListDataDefinitionInterface' - there is no single - * scalar shape for the default handler to relay. + * (e.g. a map) or a 'ListDataDefinitionInterface'. */ public function fieldIsComplexValue(FieldStorageDefinitionInterface $storage): bool; diff --git a/src/Backend/Core/Field/FileHandler.php b/src/Backend/Core/Field/FileHandler.php index b06bbe31..e01a62d6 100644 --- a/src/Backend/Core/Field/FileHandler.php +++ b/src/Backend/Core/Field/FileHandler.php @@ -49,9 +49,9 @@ protected function doExpand(array $records): array { /** * Reads the id from a saved file entity. * - * The parameter is typed 'object' so a unit-test double can stand in - * without implementing Drupal's File entity contract. The 'id()' call is - * therefore unchecked until here. + * The parameter is typed 'object' so a unit-test double need not implement + * Drupal's File entity contract. The 'method_exists()' guard is the only + * check on 'id()'. * * @param object $file * A File entity, or a File-compatible stub in tests. @@ -80,12 +80,11 @@ protected function getFieldLabel(): string { * A managed file that already exists can be referenced by its URI * ('public://foo.txt') or its bare basename ('foo.txt') without * triggering a re-upload. Paths containing '/' but no scheme (e.g. - * '/tmp/foo.txt') are treated as disk paths and fall through to the - * upload path unchanged. + * '/tmp/foo.txt') are disk paths and return NULL. * * The native return type is 'object' (not FileInterface) so a unit-test - * double that exposes only 'id()' can pass without implementing the full - * File entity contract. In production the storage returns File entities. + * double that exposes only 'id()' need not implement the full File entity + * contract. In production the storage returns File entities. * * @param string $value * The raw field value: URI, bare basename, or absolute filesystem path. diff --git a/src/Backend/Core/Field/Parser/EntityFieldParser.php b/src/Backend/Core/Field/Parser/EntityFieldParser.php index 10818c78..b9ca9b91 100644 --- a/src/Backend/Core/Field/Parser/EntityFieldParser.php +++ b/src/Backend/Core/Field/Parser/EntityFieldParser.php @@ -12,7 +12,7 @@ * Entity-field parser. * * Implements a syntax with a single uniform escape mechanism (double - * quotes) for compound values. Cells fall into two modes detected by the + * quotes) for compound values. Cells fall into 2 modes detected by the * value form, not by the spacing of separators: * * Scalar mode (no top-level 'key:"...' or 'key:[...]' pattern): @@ -22,7 +22,7 @@ * at the start of an item, where it begins a quoted string. * * Compound mode (top-level 'key:"...' or 'key:[...]' pattern present): - * - One or more 'key: value' columns separated by ','. + * - 1 or more 'key: value' columns separated by ','. * - Multi-value compound: records separated by ';'. * - Each column value MUST be a quoted string ('"..."') or token * ('[name:value]'). Bare values are a parse error. @@ -46,7 +46,7 @@ class EntityFieldParser implements EntityFieldParserInterface { protected array $ignoredProperties = []; /** - * Constructs the parser for one entity-type / bundle / classifier pairing. + * Constructs the parser for an entity-type / bundle / classifier pairing. * * @param string $entityType * The entity type ID. @@ -55,8 +55,7 @@ class EntityFieldParser implements EntityFieldParserInterface { * @param string|null $bundle * The bundle for the stub being parsed, or NULL for entity types * without bundles. When provided, bundle-scoped fields (F6-F9) are - * accepted as known fields, and their values are passed to the entity - * unchanged for the bundle's field item-list class to handle at save. + * accepted as known fields and their values are passed through unchanged. */ public function __construct( protected readonly string $entityType, @@ -126,12 +125,11 @@ public function parse(array $values): array { } } else { - // The classifier splits base fields across F1-F4 (standard, computed - // read-only, computed writable, custom storage), so all four - // predicates are checked and a computed or custom-storage base field - // like 'moderation_state' is not flagged unknown. When the bundle is - // known, F6-F9 (bundle-scoped fields) count as known too, so fields - // contributed via 'hook_entity_bundle_field_info()' are recognised. + // The classifier splits base fields across F1-F4, so all 4 predicates + // are checked and a computed or custom-storage base field like + // 'moderation_state' is known. With a bundle, F6-F9 (bundle-scoped + // fields) are known too, so a field contributed via + // 'hook_entity_bundle_field_info()' is recognised. $is_known = $this->fieldClassifier->fieldIsBaseStandard($this->entityType, $field_name) || $this->fieldClassifier->fieldIsBaseComputedReadOnly($this->entityType, $field_name) || $this->fieldClassifier->fieldIsBaseComputedWritable($this->entityType, $field_name) @@ -197,9 +195,9 @@ protected function parseCell(string $cell, bool $is_multicolumn): array { * * Compound mode is detected by the presence of a top-level * 'key:"...' or 'key:[...]' pattern - i.e. an identifier, optional - * whitespace, ':', optional whitespace, then '"' or '['. The scan - * respects quoted strings and bracketed tokens, so an embedded pattern - * inside a quoted scalar does not trigger compound mode. + * whitespace, ':', optional whitespace, then '"' or '['. The scan skips + * quoted strings and bracketed tokens, so an embedded pattern inside a + * quoted scalar does not trigger compound mode. */ protected function detectCompoundMode(string $cell): bool { $length = strlen($cell); @@ -296,10 +294,9 @@ protected function parseScalarList(string $cell): array { else { $start = $i; - // '"' is only structural at the start of an item (handled in the - // branch above). Inside an unquoted item it can be any literal - // character (e.g. an HTML attribute value), so the stop set is the - // list separator ',' and the compound-record separator ';' only. + // '"' is only structural at the start of an item. Inside an unquoted + // item it is a literal character (e.g. in an HTML attribute value), + // so the stop set is ',' and ';' only. while ($i < $length && $cell[$i] !== ',' && $cell[$i] !== ';') { $i++; } @@ -328,7 +325,6 @@ protected function parseScalarList(string $cell): array { break; } - // $cell[$i] is now ',' $i++; if ($i >= $length) { @@ -381,7 +377,7 @@ protected function parseCompound(string $cell): array { } /** - * Parses one compound record (','-separated columns) into a key/value map. + * Parses 1 compound record (','-separated columns) into a key/value map. * * @return array * The parsed columns keyed by column name. @@ -419,7 +415,7 @@ protected function parseRecord(string $record, string $cell, int $base_offset): } /** - * Parses one 'key: value' column. + * Parses 1 'key: value' column. * * @return array{0: string, 1: string} * [$key, $value] @@ -528,8 +524,8 @@ protected function splitTopLevel(string $cell, string $separator): array { /** * Advances past a '"..."' quoted string, returning the index after the close. * - * Matches the same escape grammar as 'readQuotedString()'. If the string - * is unterminated returns the cell length. + * Matches the same escape grammar as 'readQuotedString()'. For an + * unterminated string the return value is the cell length. */ protected function skipQuotedString(string $cell, int $i): int { $length = strlen($cell); @@ -554,7 +550,7 @@ protected function skipQuotedString(string $cell, int $i): int { /** * Advances past a '[...]' token, returning the index after the close. * - * If the token is unterminated returns the cell length. + * For an unterminated token the return value is the cell length. */ protected function skipToken(string $cell, int $i): int { $length = strlen($cell); @@ -571,9 +567,11 @@ protected function skipToken(string $cell, int $i): int { * Reads a '"..."' quoted string starting at the current offset. * * Advances $offset past the closing quote. Decodes the escapes \\, \", - * \n, \t, \r. Any other backslash sequence is a parse error. When called - * for cell-level diagnostics, $error_cell and $error_base_offset are used - * to report errors against the original cell rather than the fragment. + * \n, \t, \r; any other backslash sequence is a parse error. + * + * When called for cell-level diagnostics, $error_cell and + * $error_base_offset are used to report errors against the original cell + * rather than the fragment. */ protected function readQuotedString(string $fragment, int &$offset, ?string $error_cell = NULL, int $error_base_offset = 0): string { $error_cell ??= $fragment; diff --git a/src/Backend/Core/Field/Parser/EntityFieldParserInterface.php b/src/Backend/Core/Field/Parser/EntityFieldParserInterface.php index 429b16f0..1a18aac4 100644 --- a/src/Backend/Core/Field/Parser/EntityFieldParserInterface.php +++ b/src/Backend/Core/Field/Parser/EntityFieldParserInterface.php @@ -8,17 +8,17 @@ * Contract for entity-field value parsers. * * Implementations transform a raw map of field-name to cell-text pairs (as - * returned by 'EntityStubInterface::getValues()') into a final map suitable - * for handing back to 'EntityStubInterface::setValues()'. Each implementation - * owns all syntactic concerns (CSV multi-value splitting, compound column - * splitting, inline named-column interpretation, 'field:column' / ':column' - * multicolumn-header merging) and all field-type semantics (configurable vs - * base vs ignored vs unknown). + * returned by 'EntityStubInterface::getValues()') into a final map for + * 'EntityStubInterface::setValues()'. Each implementation owns all syntactic + * concerns (CSV multi-value splitting, compound column splitting, inline + * named-column interpretation, 'field:column' / ':column' multicolumn-header + * merging) and all field-type semantics (configurable vs base vs ignored vs + * unknown). * - * Heavier dependencies needed for those decisions (entity type, classifier) - * are constructor-injected. Per-call configuration that may vary between - * stubs (e.g. the list of ignored property names) is set via fluent setters - * before 'parse()' is called. + * Dependencies those decisions require (entity type, classifier) are + * constructor-injected. Per-call configuration that may vary between stubs + * (e.g. the list of ignored property names) is set via fluent setters before + * 'parse()' is called. */ interface EntityFieldParserInterface { @@ -40,8 +40,7 @@ public function parse(array $values): array; * Sets property names accepted without field-type validation. * * Backend-level creation hints on the stub (e.g. 'author', 'role', - * 'vocabulary_machine_name') are not real Drupal fields; the backend's - * create methods consume them. + * 'vocabulary_machine_name') are not Drupal fields. * * @param string[] $properties * Property names to accept without validation. diff --git a/src/Backend/Core/Field/Parser/Exception/MultipleParseException.php b/src/Backend/Core/Field/Parser/Exception/MultipleParseException.php index 2d69e164..0cf7fca6 100644 --- a/src/Backend/Core/Field/Parser/Exception/MultipleParseException.php +++ b/src/Backend/Core/Field/Parser/Exception/MultipleParseException.php @@ -7,7 +7,7 @@ /** * Container for multiple parse errors detected in a single cell. * - * Parsers collect all errors detected in one cell before throwing, so the + * Parsers collect all errors detected in 1 cell before throwing, so the * test author sees every problem at once. */ class MultipleParseException extends ParseException { @@ -16,7 +16,7 @@ class MultipleParseException extends ParseException { * Wraps multiple parse errors detected in a single cell. * * @param ParseException[] $errors - * The individual parse errors. Must contain at least one entry. + * The individual parse errors. Must contain at least 1 entry. * @param string $cell * The cell value being parsed when the errors were collected. * @param \Throwable|null $previous @@ -27,8 +27,8 @@ public function __construct(public readonly array $errors, string $cell, ?\Throw throw new \RuntimeException('MultipleParseException requires at least one error.'); } - // 'reset()' reads the first error by iteration order, because a caller - // that filtered its errors passes a list with gaps in its keys. + // 'reset()' reads the first error by iteration order, because '$errors' + // may have gaps in its keys. $first = reset($errors); parent::__construct($first->errorCode, $first->offset, $cell, $this->buildDescription($errors), NULL, $previous); diff --git a/src/Backend/Core/Field/ReferenceTarget.php b/src/Backend/Core/Field/ReferenceTarget.php index 473ccbc0..c66aab9b 100644 --- a/src/Backend/Core/Field/ReferenceTarget.php +++ b/src/Backend/Core/Field/ReferenceTarget.php @@ -6,9 +6,6 @@ /** * Immutable entity-type facts a reference field resolves its lookups against. - * - * Read once per expansion so a field holding several deltas derives the entity - * type definition a single time. */ final readonly class ReferenceTarget { diff --git a/src/Backend/Core/Field/SmartdateHandler.php b/src/Backend/Core/Field/SmartdateHandler.php index 8cca4416..cb5248d5 100644 --- a/src/Backend/Core/Field/SmartdateHandler.php +++ b/src/Backend/Core/Field/SmartdateHandler.php @@ -17,8 +17,8 @@ protected function normalize(mixed $values): array { return []; } - // A bare scalar is the start of a single delta; wrapping it here lets the - // positional branch below read it as '[start]'. + // A bare scalar is the start of a single delta, so it becomes the + // positional record '[start]'. if (!is_array($values)) { $values = [$values]; } diff --git a/src/Backend/DrupalBackend.php b/src/Backend/DrupalBackend.php index e29cb822..db8c58a6 100644 --- a/src/Backend/DrupalBackend.php +++ b/src/Backend/DrupalBackend.php @@ -99,9 +99,9 @@ public function getDrupalVersion(): int { /** * Sets the core from the current version. * - * Walks from the detected Drupal version down to the default Core class, - * using the first class that exists in the lookup chain: - * DrevOps\BehatSteps\Backend\Core{N}\Core → ... → DrevOps\BehatSteps\Backend\Core\Core. + * Uses the first class that exists in the lookup chain: + * 'DrevOps\BehatSteps\Backend\Core{N}\Core' for each major version from + * the detected one downwards, then 'DrevOps\BehatSteps\Backend\Core\Core'. * * @throws \DrevOps\BehatSteps\Backend\Exception\BootstrapException * Thrown when no Core implementation is found for the detected version. diff --git a/src/Backend/Drush/DrushResult.php b/src/Backend/Drush/DrushResult.php index 755affbf..f5c50f77 100644 --- a/src/Backend/Drush/DrushResult.php +++ b/src/Backend/Drush/DrushResult.php @@ -7,9 +7,9 @@ /** * Immutable result of a Drush command execution. * - * Pairs the process exit code with its captured standard output and standard - * error, mirroring the values a finished Symfony 'Process' exposes through - * 'getExitCode()', 'getOutput()', and 'getErrorOutput()'. + * Pairs the process exit code with its captured standard output and + * standard error. They match the values a finished Symfony 'Process' + * exposes through 'getExitCode()', 'getOutput()', and 'getErrorOutput()'. */ final readonly class DrushResult { @@ -17,7 +17,7 @@ * Constructs a DrushResult. * * @param int $exitCode - * The command exit code. Zero indicates success. + * The command exit code, 0 for success. * @param string $output * The command's captured standard output. * @param string $errorOutput diff --git a/src/Backend/DrushBackend.php b/src/Backend/DrushBackend.php index 7aabd424..f682a75e 100644 --- a/src/Backend/DrushBackend.php +++ b/src/Backend/DrushBackend.php @@ -97,7 +97,7 @@ public function __construct(?string $alias = NULL, ?string $root_path = NULL, st } /** - * Populates the creation-alias registry with aliases this backend ships. + * Populates the creation-alias registry with the backend's default aliases. * * A subclass that adds custom aliases should override this method and * call 'parent::registerDefaultCreationAliases()' first. @@ -138,7 +138,6 @@ public function processBatch(): void { * {@inheritdoc} */ public function cacheClear(?string $type = 'all'): void { - // Drush-only cache clear does not need a full rebuild. if ($type === 'drush') { $this->drush('cache-clear', ['drush'], []); return; @@ -187,8 +186,8 @@ public function configSet(string $name, string $key, mixed $value): void { * {@inheritdoc} */ public function configExists(string $name): bool { - // 'config:get' refuses an object that does not exist, so its exit code - // answers this without a second command. + // 'config:get' exits non-zero for an object that does not exist, so the + // exit code indicates existence without a second command. return $this->drushResult('config:get', [$name], ['format' => 'json'])->exitCode === 0; } @@ -205,16 +204,15 @@ public function configGetData(string $name): array { * {@inheritdoc} */ public function configSetData(string $name, array $data): void { - // 'config:set' assigns only the keys it is handed and Drush exposes no - // whole-object replace, so the object is deleted first to drop the keys - // the new data does not carry. The delete and the write are 2 commands, - // so the object is read first and put back when the write fails, rather - // than being left missing. + // Drush exposes no whole-object replace and 'config:set' assigns only the + // given keys, so the object is deleted first to drop its other keys. The + // delete and the write are 2 commands, so the previous data is read first + // and restored when the write fails. $previous = $this->configGetData($name); - // An object holding nothing has no keys to drop, and 'config:set' refuses - // to write an empty object back, so deleting one would leave nothing to - // restore from if the write then failed. + // An empty object has no keys to drop. 'config:set' rejects an empty + // object, so deleting one would leave nothing to restore when the write + // fails. if ($previous !== []) { $this->configDelete($name); } @@ -235,7 +233,7 @@ public function configSetData(string $name, array $data): void { // @codeCoverageIgnoreStart catch (\RuntimeException) { // Restoring failed as well. The write failure below is the error - // worth reporting, so this one is not allowed to replace it. + // reported, so the restore failure is discarded. } // @codeCoverageIgnoreEnd throw $e; @@ -264,7 +262,7 @@ protected function configWriteData(string $name, array $data): void { * {@inheritdoc} */ public function configDelete(string $name): void { - // 'config:delete' refuses an object that does not exist, while Drupal's + // 'config:delete' fails on an object that does not exist, while Drupal's // own config API treats deleting one as a no-op. if (!$this->configExists($name)) { return; @@ -297,8 +295,8 @@ protected function configRead(string $name, string $key, bool $with_overrides): $arguments = $key !== '' ? [$name, $key] : [$name]; $result = $this->drushResult('config:get', $arguments, $options); - // A missing object is an error to Drush and an absent value to Drupal's - // config API; the API's answer is the one the capability promises. + // A missing object is an error to Drush but an absent value to Drupal's + // config API, and the capability contract matches the API. if ($result->exitCode !== 0) { return NULL; } @@ -306,8 +304,8 @@ protected function configRead(string $name, string $key, bool $with_overrides): $decoded = json_decode(trim($result->output), TRUE); $envelope_key = $name . ':' . $key; - // Asked for a single key, 'config:get' answers with a 1-entry map keyed - // ':' rather than the bare value. + // For a single key, 'config:get' returns a 1-entry map keyed + // ':' instead of the bare value. if ($key !== '' && is_array($decoded) && array_key_exists($envelope_key, $decoded)) { return $decoded[$envelope_key]; } @@ -327,7 +325,7 @@ public function stateGet(string $name): mixed { $decoded = json_decode(trim($result->output), TRUE); - // 'state:get' answers with a 1-entry map keyed by the state key. + // 'state:get' returns a 1-entry map keyed by the state key. if (is_array($decoded) && array_key_exists($name, $decoded)) { return $decoded[$name]; } @@ -355,8 +353,8 @@ public function stateDelete(string $name): void { /** * {@inheritdoc} * - * Drush exposes no existence check over the state key-value store, so a key - * holding NULL reads the same as an absent one on this backend. + * Drush exposes no existence check over the state key-value store. A key + * holding NULL therefore reads the same as an absent one on this backend. */ public function stateExists(string $name): bool { return $this->stateGet($name) !== NULL; @@ -463,8 +461,6 @@ public function userCreate(EntityStubInterface $stub): void { $uid = $this->parseUserId($result); if (!$uid) { - // Without an id the account cannot be referenced again, so post-create - // aliases such as roles would silently never be applied. throw new \RuntimeException(sprintf("Drush did not report a user id after creating '%s'. Output: %s", $stub->getValue('name'), $result)); } diff --git a/src/Backend/Exception/CreationAliasResolutionException.php b/src/Backend/Exception/CreationAliasResolutionException.php index 1c33362f..307670b4 100644 --- a/src/Backend/Exception/CreationAliasResolutionException.php +++ b/src/Backend/Exception/CreationAliasResolutionException.php @@ -8,9 +8,9 @@ * Thrown when a creation alias cannot resolve its value. * * Raised by alias implementations when a stub property cannot be - * translated into a Drupal storage operation - for example, an 'author' - * alias referencing a username with no matching user account, or a - * 'parent' term name that does not exist in the target vocabulary. + * translated into a Drupal storage operation. Examples are an 'author' + * alias with a username that matches no account, and a 'parent' term name + * absent from the target vocabulary. */ class CreationAliasResolutionException extends Exception { } diff --git a/src/Behat/Config/ConfigSchemaReader.php b/src/Behat/Config/ConfigSchemaReader.php index cd98ad63..38ebe50a 100644 --- a/src/Behat/Config/ConfigSchemaReader.php +++ b/src/Behat/Config/ConfigSchemaReader.php @@ -19,8 +19,8 @@ class ConfigSchemaReader { /** * Declared options, keyed by context class name. * - * Reflection over every method of a context composing forty traits is too - * expensive to repeat per option read, and a class's declarations cannot + * Reflection over every method of a context composing 40 traits is too + * expensive to repeat per option read. A class's declarations cannot * change within a run. * * @var array>> diff --git a/src/Behat/Config/GroupName.php b/src/Behat/Config/GroupName.php index 9a79b521..85271254 100644 --- a/src/Behat/Config/GroupName.php +++ b/src/Behat/Config/GroupName.php @@ -7,14 +7,16 @@ /** * Converts between an option group name and the names it derives from. * - * A group is named after the trait that declares it, in snake case, and that - * trait declares its options in a method carrying the same name in camel case. - * The runtime and the documentation generator both walk that mapping, so both - * go through here and cannot disagree on it. + * A group is named after the trait that declares it, in snake case. That + * trait declares its options in a method carrying the same name in camel + * case. * - * Every conversion runs towards the group name, never back to the trait name: - * a run of capitals reads as one word, so 'APIClientTrait' and 'ApiClientTrait' - * both give 'api_client' and the group alone cannot say which was written. + * The runtime and the documentation generator both walk that mapping, so + * both go through here and cannot disagree on it. + * + * Every conversion runs towards the group name, never back to the trait name. + * A run of capitals reads as 1 word, so 'APIClientTrait' and 'ApiClientTrait' + * both give 'api_client', and the group alone does not identify the spelling. */ final class GroupName { @@ -31,8 +33,8 @@ final class GroupName { /** * Converts a camel case method prefix to its group name. * - * A run of capitals is one word, so the group a trait name derives to is the - * group its method prefix derives to: 'APIClient' and 'apiClient' both give + * A run of capitals is 1 word, so the group a trait name derives to is the + * group its method prefix derives to. 'APIClient' and 'apiClient' both give * 'api_client'. * * @param string $prefix diff --git a/src/Behat/Config/Option.php b/src/Behat/Config/Option.php index ef593d4e..7a2d5ca6 100644 --- a/src/Behat/Config/Option.php +++ b/src/Behat/Config/Option.php @@ -9,9 +9,11 @@ /** * One option a step trait declares. * - * The declared default carries the option's type: a configured value is read - * as that type, and a value that cannot be is an error naming both. A default - * of NULL names no type, so anything configured against it passes through. + * The declared default carries the option's type. A configured value is read + * as that type, and a value that cannot be is an error naming both. + * + * A default of NULL names no type, so anything configured against it passes + * through. * * @see \DrevOps\BehatSteps\Behat\Config\ConfigSchemaReader */ @@ -72,8 +74,8 @@ public function __construct( } // Every hook of the trait is switched on this one option, and the skip tag - // binds it to FALSE, so a declaration naming another type would fail at the - // hook that read it rather than here. + // binds it to FALSE. A declaration naming another type would fail at the + // hook that reads it rather than here. if ($name === self::ENABLED && !is_bool($default)) { throw new \RuntimeException(sprintf('The "%s" option switches its trait on and off, so it defaults to a boolean.', $name)); } diff --git a/src/Behat/Config/TagOverrides.php b/src/Behat/Config/TagOverrides.php index 91efae17..f0ab7fa7 100644 --- a/src/Behat/Config/TagOverrides.php +++ b/src/Behat/Config/TagOverrides.php @@ -39,8 +39,6 @@ class TagOverrides { * The value, replaced by whatever the last matching tag sets. */ public function apply(string $group, Option $option, mixed $value, array $tags): mixed { - // An 'enabled' option is also switched off by the library's one skip tag, - // named after the trait the group belongs to. $switchable = $option->name === Option::ENABLED; if ($tags === [] || ($option->tags === [] && !$switchable)) { @@ -66,9 +64,11 @@ public function apply(string $group, Option $option, mixed $value, array $tags): * Whether a skip tag names the trait that owns a group. * * The tag carries the trait's own name, which a group name cannot be - * converted back into: a run of capitals reads as one word, so 'APIClient' - * and 'ApiClient' both give 'api_client'. The tag is read forwards instead, - * and any spelling of the trait that derives the group matches it. + * converted back into. A run of capitals reads as 1 word, so 'APIClient' + * and 'ApiClient' both give 'api_client'. + * + * The tag is read forwards instead, and any spelling of the trait that + * derives the group matches it. * * @param string $tag * A tag the scenario or its feature carries, without a leading '@'. diff --git a/src/Behat/Config/TraitOptionResolver.php b/src/Behat/Config/TraitOptionResolver.php index fdb969b6..8b20a76e 100644 --- a/src/Behat/Config/TraitOptionResolver.php +++ b/src/Behat/Config/TraitOptionResolver.php @@ -12,12 +12,14 @@ * * A value is taken from the first of these that sets it: the scenario's tags, * the feature's tags, the context's 'config' argument, the extension's 'steps' - * section, the declaration's own default. The configuration layers settle when - * this object is built; the tag layers are read per option, because the tags - * belong to whichever scenario is running. + * section, the declaration's own default. * - * It holds no Behat class, reflects over nothing and knows no context beyond - * the class name it names in a failure message. + * The configuration layers settle when this object is built. The tag layers + * are read per option, because the tags belong to whichever scenario is + * running. + * + * It holds no Behat class, reflects over nothing and references no context + * beyond the class name it names in a failure message. */ class TraitOptionResolver implements TraitOptionResolverInterface { diff --git a/src/Behat/Config/TraitOptionResolverFactoryInterface.php b/src/Behat/Config/TraitOptionResolverFactoryInterface.php index fe49e52c..0f6fa9d9 100644 --- a/src/Behat/Config/TraitOptionResolverFactoryInterface.php +++ b/src/Behat/Config/TraitOptionResolverFactoryInterface.php @@ -7,9 +7,10 @@ /** * Interface for classes that build a context's option resolver. * - * A resolver depends on the context class that declared the options and on the - * 'config' argument that context was given, so it cannot be a container - * service. Registering another implementation of this interface under + * A resolver depends on the context class that declared the options and on + * that context's 'config' argument, so it cannot be a container service. + * + * Registering another implementation of this interface under * 'behat_steps.config.resolver_factory' replaces option resolution for every * context at once. */ diff --git a/src/Behat/Context/DrupalContext.php b/src/Behat/Context/DrupalContext.php index d907e0b7..2c6ca759 100644 --- a/src/Behat/Context/DrupalContext.php +++ b/src/Behat/Context/DrupalContext.php @@ -38,14 +38,16 @@ * Zero-config context carrying the whole vocabulary a Drupal suite needs. * * Extending this context is enough to write features against a Drupal site - * without writing any PHP: it is 'WebContext' plus every trait under - * 'Steps\Drupal', so a Drupal project extends one class and gets all 57 step - * traits. Each of those traits composes the helpers it needs, so the traits - * that create entities also compose the entity teardown. + * without writing any PHP. It is 'WebContext' plus every trait under + * 'Steps\Drupal', so a Drupal project extends 1 class and gets all 57 step + * traits. + * + * Each of those traits composes the helpers it needs, so the traits that + * create entities also compose the entity teardown. * * A trait for a contrib module resolves nothing until one of its steps runs, - * and then fails with a message naming the module, so composing all of them - * costs a project nothing. + * and then fails with a message naming the module. Composing all of them + * therefore costs a project nothing. * * Registering this context beside 'WebContext' is fatal, because the 28 web * traits would register their steps twice. 'WebContext::assertOneContext()' diff --git a/src/Behat/Context/WebContext.php b/src/Behat/Context/WebContext.php index 9cc866f7..87b449f1 100644 --- a/src/Behat/Context/WebContext.php +++ b/src/Behat/Context/WebContext.php @@ -44,7 +44,7 @@ * 'WebRawContext' plus every trait under 'Steps\Web'. * * A trait that can fail a scenario on a check the scenario did not ask for - * carries an 'enabled' option, so a project switches it off through + * carries an 'enabled' option. A project switches it off through * configuration rather than by composing its own context. * * A Drupal suite extends 'DrupalContext', which extends this class, so it @@ -87,12 +87,12 @@ class WebContext extends WebRawContext { /** * Rejects a suite that registers this context and a subclass of it. * - * Both register the same 28 web traits, and Behat reports that as a + * Both register the same 28 web traits. Behat reports that as a * 'RedundantStepException' naming whichever step text it reached first, * which says nothing about the cause. * * @throws \RuntimeException - * When the suite registers two contexts that both carry this class. + * When the suite registers 2 contexts that both carry this class. */ #[BeforeSuite] public static function assertOneContext(BeforeSuiteScope $scope): void { diff --git a/src/Behat/Context/WebRawContext.php b/src/Behat/Context/WebRawContext.php index 327be36d..f500811a 100644 --- a/src/Behat/Context/WebRawContext.php +++ b/src/Behat/Context/WebRawContext.php @@ -40,15 +40,15 @@ * Provides backend access, authentication delegation, option resolution, * prerequisite checks and the hook dispatcher, and composes 3 of the web * helper traits. It registers no step definitions and references no Drupal - * class beyond 'Random', which a layer lint holds. + * class beyond 'Random'. * - * Extend this to compose a context out of a chosen set of traits; extend - * 'WebContext' instead to get the whole web vocabulary, or 'DrupalContext' - * to get the Drupal vocabulary on top of it. + * A context composed out of a chosen set of traits extends this class. + * 'WebContext' adds the whole web vocabulary, and 'DrupalContext' adds the + * Drupal vocabulary on top of that. * * The helper traits it composes are on '$this' for a consuming project's own - * step definitions, and composing one again in a step trait shares the same - * state rather than duplicating it. + * step definitions. A step trait that composes one of them again shares the + * same state. * * @see \DrevOps\BehatSteps\Behat\Context\WebContext * @see \DrevOps\BehatSteps\Behat\Context\DrupalContext @@ -110,9 +110,9 @@ class WebRawContext extends RawMinkContext implements BackendAwareInterface { * does not match the type its declaration defaults to. */ public function __construct(protected array $config = []) { - // Resolved here rather than on first read, so a typo fails while Behat - // builds the context instead of at the step that would have read it. The - // extension's 'steps' section arrives later, through 'setParameters()'. + // Built here so a mistyped option fails while Behat builds the context, + // before any step reads it. The extension's 'steps' section arrives later, + // through 'setParameters()'. $this->optionResolver = $this->buildOptionResolver(); } @@ -227,8 +227,8 @@ public function httpDetachedClient(array $options = []): AbstractBrowser { * Returns a one-off browser that carries no scenario state. * * It applies the connection options the site's 'browserkit_http' session - * declares, and nothing from the scenario, so it suits requests that are - * not the visitor's own, such as fetching a script from a CDN. + * declares, and nothing from the scenario. It suits requests that are not + * the visitor's own, such as fetching a script from a CDN. * * @param array $options * Symfony HttpClient options for this browser, such as a 'timeout'. @@ -305,8 +305,8 @@ public function getBackend(string $name): BackendInterface { * Returns the highest-priority backend providing the given capability. * * A step names the capability it needs and never a backend, so the shipped - * vocabulary stays portable: a project that registers its own backend gets - * the step working as soon as that backend implements the interface. + * vocabulary stays portable. A backend a project registers serves the step + * as soon as it implements the interface. * * @param class-string $capability * The capability interface the caller needs. @@ -327,14 +327,14 @@ public function backendFor(string $capability): object { * Returns the adapter providing a browser capability for this session. * * The browser half of the vocabulary resolves its capabilities separately - * from the Drupal half: a Mink session runs exactly 1 browser driver, so + * from the Drupal half. A Mink session runs exactly 1 browser driver, so * there is no ordered list to walk and nothing to bootstrap. * * @param class-string $capability * The browser capability interface the caller needs. * * @return T - * The adapter speaking for the session's browser driver. + * The adapter for the session's browser driver. * * @throws \Behat\Mink\Exception\UnsupportedDriverActionException * When the session's browser driver does not provide the capability. @@ -348,7 +348,7 @@ public function browserDriverFor(string $capability): object { /** * Whether this session's browser driver provides a browser capability. * - * A step that degrades gracefully without the capability asks this; a step + * A step that degrades gracefully without the capability calls this; a step * that cannot proceed without it calls 'browserDriverFor()'. * * @param class-string $capability @@ -498,8 +498,10 @@ protected function buildOptionResolver(): TraitOptionResolverInterface { * * 'BEHAT_STEPS_DISABLE_CLEANUP' set to '1', 'true', 'yes' or 'on' * (case-insensitive) skips the AfterScenario teardown of entities, users - * and roles, so the state a failing scenario leaves behind can be - * inspected. The variable is not intended for CI runs. + * and roles. The state a failing scenario leaves behind can then be + * inspected. + * + * The variable is not intended for CI runs. */ protected function shouldCleanup(): bool { $env = getenv('BEHAT_STEPS_DISABLE_CLEANUP'); @@ -519,8 +521,8 @@ protected function shouldCleanup(): bool { * together, so the tag works on either line. * * A trait that declares an 'enabled' option is also switched off by that - * option, so a project turns the trait off for the whole profile or for one - * context instead of tagging every feature file. + * option. Through the option, a project turns the trait off for the whole + * profile or for 1 context instead of tagging every feature file. * * @param string $trait * The trait the hook belongs to, fully qualified or short. A hook passes @@ -548,8 +550,8 @@ protected function skipTag(string $trait, ScenarioScope $scope): bool { * Returns a backend providing a capability, reusing one already reached. * * A read-only query such as a module check returns the same result through - * any backend, so a backend the scenario already reached is returned before - * the first one in the list, and no second backend starts. + * any backend. A backend the scenario already reached is then returned + * before the first one in the list, so no second backend starts. * * @param class-string $capability * The capability interface the caller needs. @@ -571,10 +573,12 @@ protected function anyBackendFor(string $capability): object { /** * Asserts that the prerequisites a trait declares hold. * - * A trait declares them in a 'Prerequisites()' method, each naming a - * capability a backend in the scenario's list provides and, optionally, a - * check that backend passes. A backend the scenario already reached answers - * before the first one in the list, so checking never starts a second one. + * A trait declares them in a 'Prerequisites()' method. Each names + * a capability a backend in the scenario's list provides and, optionally, + * a check that backend passes. + * + * A backend the scenario already reached is used before the first one in + * the list, so checking never starts a second one. * * @param string $trait * The trait whose prerequisites to assert. A hook or a step passes @@ -596,8 +600,8 @@ protected function assertPrerequisites(string $trait): void { /** * Determines whether the prerequisites a trait declares hold. * - * A teardown asks this instead of asserting, so an unmet prerequisite never - * replaces a failure the scenario has already recorded. + * A teardown calls this instead of asserting, so an unmet prerequisite + * never replaces a failure the scenario has already recorded. * * @param string $trait * The trait whose prerequisites to check. A hook passes '__TRAIT__'. diff --git a/src/Behat/Http/HttpClientFactoryInterface.php b/src/Behat/Http/HttpClientFactoryInterface.php index 196a27d8..145d66b2 100644 --- a/src/Behat/Http/HttpClientFactoryInterface.php +++ b/src/Behat/Http/HttpClientFactoryInterface.php @@ -48,8 +48,8 @@ public function createDetached(HttpIdentity $identity, array $options = []): Abs * Returns a copy whose browsers send through a decorated transport. * * The decorator receives the shared transport, so the copy keeps the site's - * connection options. A context overriding its factory uses it to add - * retries, tracing or a mock to every detached and bare browser. + * connection options. Retries, tracing or a mock added this way apply to + * every detached and bare browser. * * @param callable(\Symfony\Contracts\HttpClient\HttpClientInterface): \Symfony\Contracts\HttpClient\HttpClientInterface $decorator * Receives the transport and returns the one to send through. diff --git a/src/Behat/Http/HttpIdentity.php b/src/Behat/Http/HttpIdentity.php index 7c92ba6d..67b9d1a4 100644 --- a/src/Behat/Http/HttpIdentity.php +++ b/src/Behat/Http/HttpIdentity.php @@ -19,7 +19,7 @@ class HttpIdentity { * Constructs an HttpIdentity object. * * @param array $cookies - * Cookie values keyed by name, in the form they travel on the wire. + * Cookie values keyed by name, in wire form. * @param string $cookieUrl * The URL the cookies were read for. They are sent to its host and that * host's subdomains only. diff --git a/src/Behat/Listener/BackendListener.php b/src/Behat/Listener/BackendListener.php index a9c95fa6..378d02b9 100644 --- a/src/Behat/Listener/BackendListener.php +++ b/src/Behat/Listener/BackendListener.php @@ -70,8 +70,9 @@ public static function getSubscribedEvents(): array { * tags name. Each group keeps the configured order, so the order the tags * are written in never changes the result. * - * The scenario's tags are published here rather than read from a hook scope, - * so a tag that sets a trait option applies to a step as well as to a hook. + * The scenario's tags are published here rather than read from a hook + * scope. A tag that sets a trait option thus applies to a step as well as + * to a hook. * * Both subscribed events carry a 'BeforeScenarioTested', an example's * scenario being the outline row itself. diff --git a/src/Behat/Manager/Authenticator.php b/src/Behat/Manager/Authenticator.php index d4ca914f..c0570c68 100644 --- a/src/Behat/Manager/Authenticator.php +++ b/src/Behat/Manager/Authenticator.php @@ -18,9 +18,9 @@ /** * Logs a user in and out of the site under test. * - * Takes a basic-auth applier rather than applying basic auth itself: a - * session reset drops request headers, so the credentials are reapplied - * afterwards, and that is the only overlap between the two concerns. + * Takes a basic-auth applier instead of applying basic auth itself. A session + * reset drops request headers, so the credentials are reapplied afterwards; + * that is the only overlap between the 2 concerns. */ class Authenticator implements AuthenticatorInterface, FastLogoutInterface { @@ -149,8 +149,6 @@ public function loggedIn(): bool { return FALSE; } - // 'getPage()' is declared non-nullable, but a stubbed session can return - // NULL, so this guard is not dead code. $page = $session->getPage(); if ($page === NULL) { return FALSE; @@ -177,9 +175,8 @@ public function loggedIn(): bool { } // As a last resort, a logout link means a user is logged in. A theme that - // defers header navigation (Critical CSS or a late JS render) may not - // have added the link yet. Poll for it within the 'login_wait' window - // that 'logIn()' also uses after submit. + // defers header navigation (Critical CSS or a late JS render) may add the + // link late, so the poll reuses the 'login_wait' window of 'logIn()'. $session->visit($this->locatePath('/')); $login_wait = (int) $this->getParameter('login_wait'); if ($login_wait > 0) { @@ -251,7 +248,7 @@ protected function backendLogin(EntityStubInterface $user): void { */ protected function backendLogout(): void { // Only a backend the scenario already reached can hold a backend session, - // and resolving one here would bootstrap it: teardown logs every scenario + // and resolving one here would bootstrap it. Teardown logs every scenario // out, so asking for the capability would boot Drupal for all of them. $this->backendRegistry->getResolvedBackendFor(AuthenticationCapabilityInterface::class)?->logout(); } diff --git a/src/Behat/Manager/BackendRegistry.php b/src/Behat/Manager/BackendRegistry.php index a80575e3..c64fc4a7 100644 --- a/src/Behat/Manager/BackendRegistry.php +++ b/src/Behat/Manager/BackendRegistry.php @@ -28,7 +28,7 @@ class BackendRegistry implements BackendRegistryInterface { protected array $scenarioBackends = []; /** - * Backends handed out during the current scenario. + * Backends resolved during the current scenario. * * @var array */ diff --git a/src/Behat/Manager/BackendRegistryInterface.php b/src/Behat/Manager/BackendRegistryInterface.php index 5b4ebc15..710e6fd2 100644 --- a/src/Behat/Manager/BackendRegistryInterface.php +++ b/src/Behat/Manager/BackendRegistryInterface.php @@ -10,11 +10,12 @@ /** * Holds the backends registered with a suite and resolves one by name. * - * Two name spaces meet here. A backend is registered under the name the + * 2 kinds of name apply. A backend is registered under the name the * extension builds it with ('drupal', 'drush', 'blackbox'), and a suite maps - * a Gherkin-facing tag name onto one of those. 'getBackend()' takes the tag - * name, because that is the name a test author writes; 'registerBackend()' - * takes the registered name. + * a Gherkin-facing tag name onto one of those. + * + * 'getBackend()' takes the tag name, because that is the name a test author + * writes; 'registerBackend()' takes the registered name. */ interface BackendRegistryInterface { @@ -39,9 +40,9 @@ public function getBackends(): array; /** * Sets the backend order the current scenario resolves against. * - * Resolution walks this order, so the first entry wins any capability it - * provides. Setting the order also clears the record of which backends the - * previous scenario resolved. + * Resolution walks this order, so the first entry providing a capability + * resolves it. Setting the order also clears the record of which backends + * the previous scenario resolved. * * @param array $backends * Ordered map of tag name to registered backend name. @@ -95,7 +96,7 @@ public function getBackendFor(string $capability): object; /** * Determines whether any backend in the scenario's order has a capability. * - * Bootstraps nothing, so a hook can call it before a scenario has used the + * Bootstraps nothing, so it is safe to call before a scenario has used the * site. * * @param class-string $capability @@ -106,9 +107,8 @@ public function hasCapability(string $capability): bool; /** * Returns a backend with the capability that this scenario already resolved. * - * Reports whether a step in this scenario resolved the capability, which is - * narrower than 'hasCapability()': a suite may list a cache-capable backend - * that no step in this scenario resolved. + * This is narrower than 'hasCapability()': a suite may list a cache-capable + * backend that no step in this scenario resolved. * * @param class-string $capability * The capability interface to look for. diff --git a/src/Behat/Manager/BasicAuthenticator.php b/src/Behat/Manager/BasicAuthenticator.php index c5ecef6a..c0f2c78b 100644 --- a/src/Behat/Manager/BasicAuthenticator.php +++ b/src/Behat/Manager/BasicAuthenticator.php @@ -11,8 +11,8 @@ /** * Applies webserver-level HTTP Basic authentication to the Mink session. * - * This is not user authentication. It carries no user, reads no site - * configuration and needs only the Mink session and the configured base URL, + * This is not user authentication: it carries no user and reads no site + * configuration. It needs only the Mink session and the configured base URL, * so a suite for a site behind basic auth uses it without any Drupal site. */ class BasicAuthenticator implements BasicAuthenticatorInterface { @@ -69,9 +69,9 @@ public function findCredentials(): ?array { $pass = parse_url($base_url, PHP_URL_PASS); return [ - // Userinfo is RFC 3986 encoded, where '+' is a literal plus and spaces - // are '%20', so decode with rawurldecode() rather than urldecode() - // (which would turn a literal '+' into a space). + // Userinfo is RFC 3986 encoded, where '+' is a literal plus and a space + // is '%20'. 'urldecode()' would turn a literal '+' into a space, so + // 'rawurldecode()' is used. 'username' => rawurldecode($name), 'password' => is_string($pass) ? rawurldecode($pass) : '', ]; diff --git a/src/Behat/Manager/BasicAuthenticatorInterface.php b/src/Behat/Manager/BasicAuthenticatorInterface.php index e72d7da6..04edcc63 100644 --- a/src/Behat/Manager/BasicAuthenticatorInterface.php +++ b/src/Behat/Manager/BasicAuthenticatorInterface.php @@ -16,8 +16,8 @@ interface BasicAuthenticatorInterface { * auth credentials. Calling this restores them so requests to sites behind * webserver-level basic auth stay authenticated after a reset. * - * Credentials come from the 'base_url' userinfo. For a driver that cannot - * set basic auth, such as a JavaScript driver, the call is a no-op. + * Credentials come from the 'base_url' userinfo. For a browser driver that + * cannot set basic auth, such as a JavaScript one, the call is a no-op. */ public function applyBasicAuth(): void; diff --git a/src/Behat/Manager/ScenarioTagRegistry.php b/src/Behat/Manager/ScenarioTagRegistry.php index a23a65df..e04b56f6 100644 --- a/src/Behat/Manager/ScenarioTagRegistry.php +++ b/src/Behat/Manager/ScenarioTagRegistry.php @@ -7,10 +7,6 @@ /** * Holds the tags the running scenario carries. * - * 'BackendListener' fills it on 'ScenarioTested::BEFORE', which Behat - * dispatches before the first 'BeforeScenario' hook, so every option read - * within the scenario sees the same tags. - * * @see \DrevOps\BehatSteps\Behat\Listener\BackendListener */ class ScenarioTagRegistry implements ScenarioTagRegistryInterface { diff --git a/src/Behat/Mink/Adapter/BrowserKitAdapter.php b/src/Behat/Mink/Adapter/BrowserKitAdapter.php index 11e89e34..c1990571 100644 --- a/src/Behat/Mink/Adapter/BrowserKitAdapter.php +++ b/src/Behat/Mink/Adapter/BrowserKitAdapter.php @@ -16,8 +16,8 @@ /** * Capabilities of a BrowserKit-based browser driver. * - * The browser driver is itself an HTTP client rather than a browser, so it - * reads cookies, sets request headers and lends out its client, but runs no + * The browser driver is itself an HTTP client rather than a browser. It reads + * cookies, sets request headers and exposes its client, but runs no * JavaScript and dispatches no key events. */ class BrowserKitAdapter extends BrowserAdapterBase implements CookieCapabilityInterface, HttpClientCapabilityInterface, RequestHeaderCapabilityInterface { diff --git a/src/Behat/Mink/BrowserAdapterBase.php b/src/Behat/Mink/BrowserAdapterBase.php index 1802817d..db0a2190 100644 --- a/src/Behat/Mink/BrowserAdapterBase.php +++ b/src/Behat/Mink/BrowserAdapterBase.php @@ -7,7 +7,7 @@ use Behat\Mink\Driver\DriverInterface; /** - * Holds the browser driver an adapter speaks for. + * Holds the browser driver an adapter wraps. */ abstract class BrowserAdapterBase implements BrowserAdapterInterface { diff --git a/src/Behat/Mink/BrowserAdapterInterface.php b/src/Behat/Mink/BrowserAdapterInterface.php index 7c8e39c5..9e018c19 100644 --- a/src/Behat/Mink/BrowserAdapterInterface.php +++ b/src/Behat/Mink/BrowserAdapterInterface.php @@ -9,14 +9,14 @@ /** * Declares which capabilities a browser driver provides. * - * Browser drivers ship from other packages, so one cannot implement the + * Browser drivers are defined in other packages, so one cannot implement the * capability interfaces itself. An adapter implements them on its behalf and - * states which browser driver it speaks for. + * declares which browser driver it supports. */ interface BrowserAdapterInterface { /** - * Whether this adapter speaks for the given browser driver. + * Whether this adapter supports the given browser driver. * * @param \Behat\Mink\Driver\DriverInterface $driver * The browser driver a session is running. diff --git a/src/Behat/Mink/BrowserCapabilityResolver.php b/src/Behat/Mink/BrowserCapabilityResolver.php index 266693e8..5403a94d 100644 --- a/src/Behat/Mink/BrowserCapabilityResolver.php +++ b/src/Behat/Mink/BrowserCapabilityResolver.php @@ -11,17 +11,17 @@ use DrevOps\BehatSteps\Behat\Mink\Adapter\Selenium2Adapter; /** - * Answers what a session's browser driver can do. + * Resolves the capabilities of a session's browser driver. * * Mirrors 'BackendRegistry::getBackendFor()' on the Drupal side: a step names - * the capability it needs and never a browser driver, so a project - * registering its own browser driver gets the shipped steps working as soon - * as it registers an adapter declaring that capability. + * the capability it needs and never a browser driver. A project registering + * its own browser driver therefore runs the shipped steps as soon as it + * registers an adapter declaring that capability. */ class BrowserCapabilityResolver { /** - * Adapter classes, in the order they are offered a browser driver. + * Adapter classes, in the order they are tried for a browser driver. * * @var array> */ @@ -32,7 +32,7 @@ class BrowserCapabilityResolver { ]; /** - * Adapters already built, keyed by the browser driver they speak for. + * Adapters already built, keyed by the browser driver they wrap. * * Keyed by the object rather than its id, because PHP reuses an object id * once the object it belonged to is collected. @@ -52,7 +52,7 @@ public function __construct() { * Registers an adapter class ahead of the shipped ones. * * @param class-string<\DrevOps\BehatSteps\Behat\Mink\BrowserAdapterInterface> $adapter - * The adapter class to offer a browser driver first. + * The adapter class to try first for a browser driver. */ public function registerAdapter(string $adapter): void { array_unshift($this->adapters, $adapter); @@ -89,8 +89,8 @@ public function resolve(DriverInterface $driver, string $capability): object { * Whether the given browser driver provides a capability. * * Pairs with 'resolve()' the way 'BackendRegistryInterface::hasCapability()' - * pairs with 'getBackendFor()': a step that degrades gracefully asks this, - * and a step that cannot proceed without the capability calls 'resolve()'. + * pairs with 'getBackendFor()': this fits a capability the caller can do + * without, and 'resolve()' one it cannot. * * @param \Behat\Mink\Driver\DriverInterface $driver * The browser driver the session is running. @@ -102,7 +102,7 @@ public function has(DriverInterface $driver, string $capability): bool { } /** - * Returns the adapter speaking for a browser driver, or NULL when none does. + * Returns the adapter for a browser driver, or NULL when none supports it. * * @param \Behat\Mink\Driver\DriverInterface $driver * The browser driver the session is running. diff --git a/src/Behat/Mink/Capability/CookieCapabilityInterface.php b/src/Behat/Mink/Capability/CookieCapabilityInterface.php index dce04a30..38c09493 100644 --- a/src/Behat/Mink/Capability/CookieCapabilityInterface.php +++ b/src/Behat/Mink/Capability/CookieCapabilityInterface.php @@ -12,14 +12,14 @@ interface CookieCapabilityInterface { /** * Returns every cookie the browser currently holds, 1 per name. * - * Values come back in wire form, exactly as the browser stores them, so a - * caller building a 'Cookie' header passes them straight through and a - * caller asserting on a value decodes first. + * Values are in wire form, exactly as the browser stores them, so they fit + * a 'Cookie' header as is and need decoding before an assertion. * - * The name and the value are the whole contract. A BrowserKit cookie jar - * holds several objects per name across domains and paths and exposes only - * the resolved value for a URL, not the object it resolved, so any further - * attribute would be reliable on some browser drivers and a guess on others. + * The name and the value are the whole contract, because any further + * attribute would be reliable on some browser drivers and a guess on + * others. A BrowserKit cookie jar holds several objects per name across + * domains and paths and exposes only the value resolved for a URL, not the + * object. * * @return array * One entry per cookie name. diff --git a/src/Behat/Mink/Capability/HttpClientCapabilityInterface.php b/src/Behat/Mink/Capability/HttpClientCapabilityInterface.php index 7f309784..6e44be92 100644 --- a/src/Behat/Mink/Capability/HttpClientCapabilityInterface.php +++ b/src/Behat/Mink/Capability/HttpClientCapabilityInterface.php @@ -7,10 +7,10 @@ use Symfony\Component\BrowserKit\AbstractBrowser; /** - * Capability: lend out the browser the Mink session drives. + * Capability: expose the browser the Mink session drives. * * A request sent through that browser becomes the page the session holds. A - * browser driver speaking to a real browser has no such browser in PHP, so + * browser driver controlling a real browser has no such browser in PHP, so * only a browser driver that is itself an HTTP client provides this. * * @see \DrevOps\BehatSteps\Behat\Context\WebRawContext::httpPageClient() diff --git a/src/Behat/Mink/Capability/JavascriptCapabilityInterface.php b/src/Behat/Mink/Capability/JavascriptCapabilityInterface.php index 55af4536..c372c5db 100644 --- a/src/Behat/Mink/Capability/JavascriptCapabilityInterface.php +++ b/src/Behat/Mink/Capability/JavascriptCapabilityInterface.php @@ -8,10 +8,11 @@ * Capability: the browser evaluates JavaScript. * * The interface declares no methods. A step executes its script through the - * Mink session's own API, so the only question an adapter answers here is - * whether the browser driver runs JavaScript at all; adding 'execute' and - * 'evaluate' methods no caller would use would repeat the unused-capability - * problem this layer exists to remove. + * Mink session's own API, so an adapter only states whether the browser + * driver runs JavaScript at all. + * + * 'execute' and 'evaluate' methods would have no caller, so they would add + * the unused-capability problem this layer removes. */ interface JavascriptCapabilityInterface { diff --git a/src/Behat/Mink/Capability/RequestHeaderCapabilityInterface.php b/src/Behat/Mink/Capability/RequestHeaderCapabilityInterface.php index 66eca796..97d41f70 100644 --- a/src/Behat/Mink/Capability/RequestHeaderCapabilityInterface.php +++ b/src/Behat/Mink/Capability/RequestHeaderCapabilityInterface.php @@ -8,7 +8,7 @@ * Capability: set a header the browser sends with each request. * * A WebDriver session cannot add request headers, so a browser driver that - * speaks WebDriver does not provide this. + * uses WebDriver does not provide this. */ interface RequestHeaderCapabilityInterface { diff --git a/src/Behat/Mink/Element/DocumentElement.php b/src/Behat/Mink/Element/DocumentElement.php index 616fb256..aa2fcc05 100644 --- a/src/Behat/Mink/Element/DocumentElement.php +++ b/src/Behat/Mink/Element/DocumentElement.php @@ -10,12 +10,12 @@ /** * Document element that reads page text the way a browser renders it. * - * Registered as a 'class_alias' over Mink's own 'DocumentElement', so it must - * extend 'TraversableElement' rather than that class: the alias is installed - * before the Mink class is autoloaded. + * Registered as a 'class_alias' over Mink's own 'DocumentElement'. The alias + * is installed before the Mink class is autoloaded, so this class extends + * 'TraversableElement' and not the class it replaces. * - * Under BrowserKit, Mink reads the text of the '//html' node, which counts the - * contents of '' and Drupal's settings JSON as page text, and throws + * Under BrowserKit, Mink reads the text of the '//html' node, so the contents + * of '' and Drupal's settings JSON count as page text. It also throws * outright on a response that is not HTML. * * @see https://github.com/minkphp/MinkBrowserKitDriver/issues/153 diff --git a/src/Behat/Mink/ServiceContainer/Driver/BrowserKitFactory.php b/src/Behat/Mink/ServiceContainer/Driver/BrowserKitFactory.php index d27bed63..76c10f5c 100644 --- a/src/Behat/Mink/ServiceContainer/Driver/BrowserKitFactory.php +++ b/src/Behat/Mink/ServiceContainer/Driver/BrowserKitFactory.php @@ -61,7 +61,7 @@ public function getClientOptions(): array { } /** - * Sorts options by key at every level, so key order never tells 2 apart. + * Sorts options by key at every level, so comparisons ignore key order. * * @param array $options * The options to sort. diff --git a/src/Behat/MinkAwareTrait.php b/src/Behat/MinkAwareTrait.php index 400601d5..a45ead9d 100644 --- a/src/Behat/MinkAwareTrait.php +++ b/src/Behat/MinkAwareTrait.php @@ -49,8 +49,8 @@ public function getMink(): Mink { * Returns the Mink session. * * @param string|null $name - * The name of the session to return. If omitted the active session will - * be returned. + * The name of the session to return. If omitted, the active session is + * returned. */ public function getSession(?string $name = NULL): Session { return $this->getMink()->getSession($name); @@ -96,8 +96,8 @@ public function setMinkParameter(string $name, mixed $value): void { * Returns the Mink session assertion tool. * * @param string|null $name - * The name of the session to return. If omitted the active session will - * be returned. + * The name of the session to return. If omitted, the active session is + * returned. */ public function assertSession(?string $name = NULL): WebAssert { return $this->getMink()->assertSession($name); @@ -113,7 +113,8 @@ public function visitPath(string $path, ?string $session_name = NULL): void { /** * Locates a URL, based on the provided path. * - * Override to provide a custom routing mechanism. + * A class composing this trait overrides this method to provide a custom + * routing mechanism. */ public function locatePath(string $path): string { // Only a full 'http://' or 'https://' scheme makes the path absolute, so diff --git a/src/Behat/ParametersTrait.php b/src/Behat/ParametersTrait.php index 74d98c7c..2f5e9a78 100644 --- a/src/Behat/ParametersTrait.php +++ b/src/Behat/ParametersTrait.php @@ -13,9 +13,11 @@ * * Any context reads parameters, text and selectors through this trait, whether * or not it extends 'WebRawContext'. A context implements - * 'ParametersAwareInterface' and composes this trait; 'BackendAwareInitializer' - * then injects the parameter array through 'setParameters()' before any - * scenario runs. No backend bootstrap is required. + * 'ParametersAwareInterface' and composes this trait. + * + * 'BackendAwareInitializer' then injects the parameter array through + * 'setParameters()' before any scenario runs. No backend bootstrap is + * required. * * @see \DrevOps\BehatSteps\Behat\ServiceContainer\BehatStepsExtension */ @@ -65,7 +67,7 @@ public function getParameter(string $name): mixed { * The text value. * * @throws \RuntimeException - * Thrown when the text is not present in the list of parameters. + * When the text is not present in the list of parameters. */ public function getDrupalText(string $name): string { $text = $this->getParameter('text'); @@ -86,7 +88,7 @@ public function getDrupalText(string $name): string { * The CSS selector. * * @throws \RuntimeException - * Thrown when the selector is not present in the list of parameters. + * When the selector is not present in the list of parameters. */ public function getDrupalSelector(string $name): string { $selectors = $this->getParameter('selectors'); diff --git a/src/Behat/Prerequisite/Prerequisite.php b/src/Behat/Prerequisite/Prerequisite.php index eba82386..8597e176 100644 --- a/src/Behat/Prerequisite/Prerequisite.php +++ b/src/Behat/Prerequisite/Prerequisite.php @@ -7,10 +7,11 @@ /** * One prerequisite a trait declares. * - * A prerequisite is stated through a backend capability: a backend in the - * scenario's list provides the capability, and for a check, that backend passes - * the check. A trait returns its prerequisites from a 'Prerequisites()' - * method. + * A prerequisite is stated through a backend capability. A backend in the + * scenario's list provides the capability, and for a check, that backend + * passes the check. + * + * A trait returns its prerequisites from a 'Prerequisites()' method. * * @see \DrevOps\BehatSteps\Behat\Prerequisite\PrerequisiteReader */ @@ -110,7 +111,7 @@ public static function capability(string $capability, string $description = ''): * Declares a check a backend providing a capability passes. * * The capability is the type of the closure's only parameter, so it cannot - * drift from what the closure calls. + * differ from what the closure calls. * * @param \Closure $check * A static closure taking the backend, typed to the capability interface it diff --git a/src/Behat/Selector/RegionSelector.php b/src/Behat/Selector/RegionSelector.php index 02bdf655..61b52476 100644 --- a/src/Behat/Selector/RegionSelector.php +++ b/src/Behat/Selector/RegionSelector.php @@ -19,7 +19,7 @@ class RegionSelector implements SelectorInterface { * Constructs a RegionSelector. * * @param \Behat\Mink\Selector\CssSelector $cssSelector - * The CSS selector that performs the actual CSS-to-XPath translation. + * The CSS selector that translates CSS to XPath. * @param array $regions * Map of region names to CSS selectors, sourced from the extension's * 'regions' configuration. diff --git a/src/Behat/ServiceContainer/BehatStepsExtension.php b/src/Behat/ServiceContainer/BehatStepsExtension.php index 6ac79dd7..46ed22ef 100644 --- a/src/Behat/ServiceContainer/BehatStepsExtension.php +++ b/src/Behat/ServiceContainer/BehatStepsExtension.php @@ -278,9 +278,8 @@ protected function loadParameters(ContainerBuilder $container, array $config): v $regions = $config['regions'] ?? []; - // Mirror the map into the config so the 'behat_steps.parameters' and - // 'behat_steps.regions' container parameters always expose the same value, - // even when the optional 'regions' key was omitted from the configuration. + // Mirror the map into the config so 'behat_steps.parameters' and + // 'behat_steps.regions' expose the same value when 'regions' is omitted. $config['regions'] = $regions; $container->setParameter('behat_steps.parameters', $config); @@ -416,7 +415,7 @@ public static function resolveBinaryPath(string $binary): string { return $candidate; } - // Probe the parent directory, which covers a working directory one level + // Probe the parent directory, which covers a working directory 1 level // deep such as a Drupal root inside a project. $candidate = dirname($cwd) . '/' . $binary; if (file_exists($candidate)) { @@ -477,9 +476,9 @@ protected function processBackends(ContainerBuilder $container): void { foreach ($backends as $tag => $name) { $tag = $this->validateBackendEntry($tag, $name, $registered); - // Resolution lowercases a name, so two entries differing only by case - // would collapse into one and the later would silently take the - // earlier's place in the order. + // Resolution lowercases a name, so 2 entries differing only by case + // would collapse into 1 and the later would silently replace the + // earlier in the order. if (isset($seen[$tag])) { throw new InvalidConfigurationException(sprintf('The "backends" list under "%s" names "%s" twice. A name is matched without regard to case, so it may appear only once.', self::CONFIG_KEY, $tag)); } @@ -513,8 +512,8 @@ protected function validateBackendEntry(int|string $tag, mixed $name, array $reg $tag = strtolower(is_int($tag) ? $name : $tag); // A tag name is typed into a feature file after '@backend:', so it cannot - // carry whitespace or a second colon. '\z' rather than '$', which would - // also match before a trailing newline and let one through. + // carry whitespace or a second colon. '$' would also match before a + // trailing newline, so the pattern ends in '\z'. if (preg_match('/^[a-z0-9_-]+\z/', $tag) !== 1) { throw new InvalidConfigurationException(sprintf('The "backends" list under "%s" names a backend "%s". A backend name may hold only letters, digits, "_" and "-", so that "@backend:%s" is a valid tag.', self::CONFIG_KEY, $tag, $tag)); } @@ -549,8 +548,8 @@ protected function processHttpClient(ContainerBuilder $container): void { * Switches to the custom class generator. * * Behat collects generators by tag before an activated extension's - * 'process()' runs, and it collects them as references to a service id, so - * replacing the definition behind that id swaps the class in place. + * 'process()' runs, as references to a service id. Replacing the definition + * behind that id swaps the class in place. */ protected function processClassGenerator(ContainerBuilder $container): void { $definition = new Definition(ClassGenerator::class); diff --git a/src/Behat/Tag.php b/src/Behat/Tag.php index 5bfeb820..4846373a 100644 --- a/src/Behat/Tag.php +++ b/src/Behat/Tag.php @@ -15,10 +15,12 @@ * 'TaggedNodeInterface::hasTag()' compares strictly. Every tag this library * reads goes through here, so a tag matches on both majors. * - * 'has()', 'values()' and 'valueStates()' take a subject: a scenario scope or - * event reads the scenario together with its feature, and a node reads that - * node alone. A parametrized tag reads '@:', and a '!' before - * the value switches it off, as in '@module:!help'. + * 'has()', 'values()' and 'valueStates()' take a subject. A scenario scope + * or event reads the scenario together with its feature, and a node reads + * that node alone. + * + * A parametrized tag reads '@:', and a '!' before the value + * switches it off, as in '@module:!help'. */ final class Tag { diff --git a/src/Helper/Drupal/AuthTrait.php b/src/Helper/Drupal/AuthTrait.php index dda9f60b..6b071688 100644 --- a/src/Helper/Drupal/AuthTrait.php +++ b/src/Helper/Drupal/AuthTrait.php @@ -54,9 +54,9 @@ trait AuthTrait { /** * Removes any created users. * - * The early-return guard also skips the logout below, because * 'BEHAT_STEPS_DISABLE_CLEANUP' leaves the failing scenario's state intact, - * session included. Later scenarios in the same run inherit that login. + * session included, so the early-return guard skips the logout as well. + * Later scenarios in the same run inherit that login. */ #[AfterScenario] public function authCleanUsers(AfterScenarioScope $scope): void { @@ -82,9 +82,9 @@ public function authCleanUsers(AfterScenarioScope $scope): void { $user_registry->clearUsers(); } - // Reset auth state even when the scenario created no users: a scenario - // may log in as a pre-existing user without calling userCreate(), leaving - // stale session state for the next scenario. + // A scenario can log in as a pre-existing user without calling + // userCreate(), so the auth state is reset even when it created no users. + // Otherwise the next scenario starts with stale session state. if ($this->authGetAuthenticator() instanceof FastLogoutInterface) { $this->authLogout(TRUE); } @@ -178,8 +178,8 @@ public function authUserCreate(EntityStubInterface $stub): EntityStubInterface { $backend->userCreate($stub); $this->entityLifecycleRestoreScalarBaseFields($stub, $scalars); - // Register before the post-create hooks run: a hook that throws still - // leaves the user behind, and cleanup removes only registered stubs. + // Cleanup removes only registered stubs. A post-create hook that throws + // leaves the saved user in place, so the stub is registered first. $this->authGetUserRegistry()->addUser($stub); $this->entityLifecycleDispatchHooks(AfterUserCreateScope::class, $stub); diff --git a/src/Helper/Drupal/EntityLifecycleTrait.php b/src/Helper/Drupal/EntityLifecycleTrait.php index d203ba53..706089ec 100644 --- a/src/Helper/Drupal/EntityLifecycleTrait.php +++ b/src/Helper/Drupal/EntityLifecycleTrait.php @@ -42,7 +42,7 @@ trait EntityLifecycleTrait { /** - * The tag that keeps the entities of the type it names after the scenario. + * The tag that names an entity type excluded from cleanup. */ protected const ENTITY_LIFECYCLE_CLEANUP_SKIP_TAG = 'behat-steps-entity-cleanup-skip'; @@ -66,9 +66,8 @@ trait EntityLifecycleTrait { public static function entityLifecycleAlterNodeParameters(BeforeNodeCreateScope $scope): void { $stub = $scope->getStub(); - // A backend that writes the node over the command line takes the values as - // written, so string dates are converted only for a backend that saves them - // through Drupal's own storage. + // A command-line backend takes the values as written, so string dates are + // converted only for a backend that saves through Drupal's own storage. $context = $scope->getContext(); if (!$context instanceof BackendAwareInterface) { @@ -109,7 +108,7 @@ public static function entityLifecycleAlterNodeParameters(BeforeNodeCreateScope * node referencing a term is deleted before the entity it references. * * '@behat-steps-skip:EntityLifecycleTrait' skips the whole pass, and - * '@behat-steps-entity-cleanup-skip:' skips one entity + * '@behat-steps-entity-cleanup-skip:' skips 1 entity * type. */ #[AfterScenario] @@ -394,8 +393,6 @@ protected function entityLifecycleDispatchHooks(string $scope_class, EntityStubI $scope = new $scope_class($environment, $this, $stub); $call_results = $this->dispatcher->dispatchScopeHooks($scope); - // The dispatcher collects exceptions rather than raising them, so the - // first one is rethrown here. foreach ($call_results as $call_result) { $exception = $call_result->getException(); @@ -452,7 +449,7 @@ protected function entityLifecycleGetFieldParser(string $entity_type, FieldClass * * Accepts either the machine name (returned as-is) or the human label * (looked up via the vocabulary storage). Falls back to the original value - * when no label matches, leaving the backend to surface a not-found error. + * when no label matches, so the backend reports a not-found error. */ protected function entityLifecycleResolveVocabularyMachineName(string $identifier): string { $this->backendFor(CoreCapabilityInterface::class); @@ -473,10 +470,10 @@ protected function entityLifecycleResolveVocabularyMachineName(string $identifie /** * Captures the scalar values on an entity stub. * - * The backend runs base fields through the field-handler pipeline during - * create, which casts scalar values such as 'title', 'name', 'mail' or - * 'pass' to single-element arrays. Downstream code expects scalars, so the - * values are captured before the backend call and restored after it. + * During create, the backend's field-handler pipeline casts scalar + * base-field values such as 'title', 'name', 'mail' or 'pass' to + * single-element arrays. Downstream code expects scalars, so the values are + * captured before the backend call and restored after it. * * @param \DrevOps\BehatSteps\Backend\Entity\EntityStubInterface $stub * The entity stub to inspect. diff --git a/src/Helper/Drupal/FixtureFileTrait.php b/src/Helper/Drupal/FixtureFileTrait.php index 666874e1..35fc4427 100644 --- a/src/Helper/Drupal/FixtureFileTrait.php +++ b/src/Helper/Drupal/FixtureFileTrait.php @@ -76,13 +76,10 @@ public function fixtureFileExpandEntityFields(string $entity_type, EntityStubInt // Parsed shapes produced by 'EntityFieldParser' or the legacy parser: // - scalar: 'foo.jpg' (treated as single-value) // - scalar list: ['foo.jpg', 'bar.jpg'] (multi-value) - // - keyed record: ['target_id' => 'foo.jpg', 'alt' => 'A'] (single compound) - // - list of records: [['target_id' => 'foo.jpg', 'alt' => 'A'], ...] (multi-value compound) - // - // Numerically-indexed arrays (lists) are iterated element-by-element so - // every delta is resolved. Keyed records and bare scalars are wrapped - // in a single-element list, processed once, and unwrapped when written - // back to the stub. + // - keyed record: ['target_id' => 'foo.jpg', 'alt' => 'A'] + // (single compound) + // - list of records: [['target_id' => 'foo.jpg', 'alt' => 'A'], ...] + // (multi-value compound) $is_list = is_array($value) && array_is_list($value); $records = $is_list ? $value : [$value]; $mutated = FALSE; diff --git a/src/Helper/Web/RequestHeadersTrait.php b/src/Helper/Web/RequestHeadersTrait.php index af0490e0..7dc75643 100644 --- a/src/Helper/Web/RequestHeadersTrait.php +++ b/src/Helper/Web/RequestHeadersTrait.php @@ -7,7 +7,7 @@ /** * Holds the request headers shared by the traits that issue HTTP requests. * - * One array is shared by every composing trait, so a header set by one trait + * 1 array is shared by every composing trait, so a header set by one trait * is available to the trait that sends the request whether or not the * context composes both. */ diff --git a/src/Helper/Web/TableTransposeTrait.php b/src/Helper/Web/TableTransposeTrait.php index 10a775d8..c19077f4 100644 --- a/src/Helper/Web/TableTransposeTrait.php +++ b/src/Helper/Web/TableTransposeTrait.php @@ -7,18 +7,18 @@ use Behat\Gherkin\Node\TableNode; /** - * Reads a vertical Gherkin table as one set of values per entity. + * Reads a vertical Gherkin table as 1 set of values per entity. * - * A vertical table names a field per row and carries one column of values - * per entity. It reads better than a wide horizontal table when an entity - * has many fields. + * A vertical table names a field per row and carries 1 column of values per + * entity. It reads better than a wide horizontal table when an entity has + * many fields. */ trait TableTransposeTrait { /** * Transpose a vertical table format (field/value columns) to entity arrays. * - * Supports both single and multiple entity creation: + * Supports both a single entity and multiple entities: * * Single entity (2 columns): * | name | John | diff --git a/src/Steps/Drupal/BatchTrait.php b/src/Steps/Drupal/BatchTrait.php index 9fab503c..e597dc84 100644 --- a/src/Steps/Drupal/BatchTrait.php +++ b/src/Steps/Drupal/BatchTrait.php @@ -9,7 +9,7 @@ /** * Wait for Drupal's Batch API to finish. * - * - Poll the batch progress element until it leaves the page. + * - Poll the batch progress element until the page no longer contains it. * * A batch page reloads itself until the operation completes, so a following * assertion would otherwise read the progress screen rather than the result. diff --git a/src/Steps/Drupal/BigPipeTrait.php b/src/Steps/Drupal/BigPipeTrait.php index aaff7612..db17ad19 100644 --- a/src/Steps/Drupal/BigPipeTrait.php +++ b/src/Steps/Drupal/BigPipeTrait.php @@ -21,18 +21,19 @@ * replacements complete fails intermittently with "element not found". * * With this trait included, every `@javascript` scenario waits before each - * step until no BigPipe placeholder marker remains in the DOM, which removes - * that race without an explicit step. + * step until no BigPipe placeholder marker remains in the DOM. The wait + * removes the race without an explicit step. * * The wait is best-effort: on timeout the step still runs, so a placeholder * that is never replaced fails the following assertion rather than the wait. * * A browser driver that runs no JavaScript never replaces those placeholders, * and does not follow the `http-equiv=refresh` fallback either. An - * authenticated-user assertion on such a browser driver silently misses - * whatever BigPipe deferred. A scenario tagged `@bigpipe` gets the - * `big_pipe_nojs` cookie, which makes Drupal render the page in full - * server-side. + * authenticated-user assertion on such a browser driver silently misses the + * content BigPipe deferred. + * + * A scenario tagged `@bigpipe` gets the `big_pipe_nojs` cookie, so Drupal + * renders the page in full server-side. * * Skip processing with tag: `@behat-steps-skip:BigPipeTrait`. * @@ -40,7 +41,7 @@ * - `@bigpipe` - render server-side on a browser driver without JavaScript. * * Set the `big_pipe.wait_timeout` option to change the maximum wait, or assign - * `$bigPipeWaitTimeout` to override it for one scenario. + * `$bigPipeWaitTimeout` to override it for 1 scenario. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ @@ -65,7 +66,7 @@ trait BigPipeTrait { protected bool $bigPipeAutoWaitEnabled = FALSE; /** - * Whether the scenario asked for server-side rendering. + * Whether server-side rendering is enabled for the scenario. */ protected bool $bigPipeServerRenderEnabled = FALSE; @@ -130,7 +131,7 @@ public function bigPipeWaitForPlaceholders(int $timeout_ms): void { } /** - * Set the no-JS cookie when the scenario asked for server-side rendering. + * Set the no-JS cookie for a scenario with server-side rendering enabled. * * A browser driver that runs JavaScript replaces the placeholders itself, so * the cookie is only for the ones that do not. 'setCookie()' is idempotent, @@ -141,8 +142,6 @@ protected function bigPipeApplyServerRenderCookie(): void { return; } - // The probe runs once a scenario: a browser driver does not gain or lose - // script support between steps. $this->bigPipeJavascriptProbe ??= $this->bigPipeJavascriptIsSupported(); if ($this->bigPipeJavascriptProbe === TRUE) { diff --git a/src/Steps/Drupal/ConfigOverrideTrait.php b/src/Steps/Drupal/ConfigOverrideTrait.php index bdc6d90a..71c8d3ca 100644 --- a/src/Steps/Drupal/ConfigOverrideTrait.php +++ b/src/Steps/Drupal/ConfigOverrideTrait.php @@ -112,9 +112,8 @@ public function configOverrideBeforeScenario(BeforeScenarioScope $scope): void { #[BeforeStep] public function configOverrideBeforeStep(BeforeStepScope $scope): void { if ($this->configOverrideDisabledNames === []) { - // Nothing to propagate. The process-level signal persists beyond the - // scenario that set it, so it is cleared here along with the browser - // driver's header. + // The process-level signal persists beyond the scenario that set it, so + // it is cleared here along with the browser driver's header. $this->configOverrideClearSignal(); $this->configOverrideClearBrowserDriverHeader(); diff --git a/src/Steps/Drupal/DrushTrait.php b/src/Steps/Drupal/DrushTrait.php index c64a8e0e..88e218ac 100644 --- a/src/Steps/Drupal/DrushTrait.php +++ b/src/Steps/Drupal/DrushTrait.php @@ -16,9 +16,9 @@ * - Run a command that is expected to fail and keep its output. * - Assert the last command's output by substring or regular expression. * - * Steps resolve the backend that can run Drush commands rather than the one at - * the front of the scenario's order, so they work in a scenario driven by any - * other backend as long as the suite lists a Drush-capable one. + * Steps resolve the backend that can run Drush commands, not the first one in + * the scenario's order. They work in a scenario driven by any other backend + * as long as the suite lists a Drush-capable one. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ @@ -137,8 +137,8 @@ public function drushAssertOutputMatches(string $pattern): void { $output = $this->drushReadOutput(); $result = @preg_match($pattern, $output); - // A malformed pattern also returns FALSE, so it is reported apart from an - // output that did not match. + // 'preg_match()' returns FALSE for a malformed pattern, so that case is + // reported apart from an output that did not match. if ($result === FALSE) { throw new \RuntimeException(sprintf('"%s" is not a valid regular expression: %s.', $pattern, preg_last_error_msg())); } @@ -187,8 +187,8 @@ public function drushRunExpectingFailure(string $command, ?string $arguments = N $args = $arguments === NULL ? [] : [$this->drushFixArgument($arguments)]; $result = $this->drushGetBackend()->drushResult($command, $args); - // Prefer stdout and fall back to stderr. The success path returns whatever - // the command wrote, and the failure path matches it. + // The success path returns the command's output, so the failure path keeps + // stdout and falls back to stderr when it is empty. $output = $result->output === '' ? $result->errorOutput : $result->output; $this->drushOutput = $output; diff --git a/src/Steps/Drupal/EmailTrait.php b/src/Steps/Drupal/EmailTrait.php index fefc28e5..039d812b 100644 --- a/src/Steps/Drupal/EmailTrait.php +++ b/src/Steps/Drupal/EmailTrait.php @@ -92,8 +92,8 @@ public function emailAfterScenario(AfterScenarioScope $scope): void { return; } - // The step can enable the system without the '@email' tag, so the enabled - // handler types decide the teardown. + // The step can enable the system without the '@email' tag, so the teardown + // checks the enabled handler types instead. if ($this->emailHandlerTypes === []) { return; } @@ -178,8 +178,8 @@ public function emailFollowLinkContaining(string $partial_url): void { foreach ($this->emailGetCollectedMessages() as $message) { $body = $message['params']['body'] ?? NULL; - // A handler that puts a structure in 'params.body' leaves the rendered - // text in 'body', so fall through rather than skipping the message. + // A handler that stores a structure in 'params.body' leaves the rendered + // text in 'body', so 'body' is read in that case. if (!is_string($body)) { $body = $message['body'] ?? ''; } @@ -252,10 +252,10 @@ public function emailFollowLinkNumberWithSubjectContaining(string $index, string /** * Enable the test email system. * - * Collects with the handler types the scenario's `@email:TYPE` tags name, or - * with the `default` handler when none do. The system is disabled again once - * the scenario finishes, unless `@behat-steps-skip:EmailTrait` switches the - * trait's hooks off. + * Collects with the handler types named by the scenario's `@email:TYPE` tags, + * or with the `default` handler when none are named. The system is disabled + * again once the scenario finishes, unless `@behat-steps-skip:EmailTrait` + * switches the trait's hooks off. * * @code * When I enable the test email system @@ -275,8 +275,7 @@ public function emailEnableTestSystem(): void { $this->emailSetMailSystemDefault($type, 'test_mail_collector'); } - // Clearing here lets this step definition be reused to clear existing - // mail. + // Clearing on enable lets this step also reset existing mail. $this->emailClearTestQueue(TRUE); } diff --git a/src/Steps/Drupal/EntityTrait.php b/src/Steps/Drupal/EntityTrait.php index 427cd70c..2b068c04 100644 --- a/src/Steps/Drupal/EntityTrait.php +++ b/src/Steps/Drupal/EntityTrait.php @@ -19,7 +19,7 @@ * created here are removed after the scenario along with every other entity * the scenario created. * - * Skip cleanup for one type with tag: + * Skip cleanup for 1 type with tag: * `@behat-steps-entity-cleanup-skip:commerce_product`. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext diff --git a/src/Steps/Drupal/LanguageTrait.php b/src/Steps/Drupal/LanguageTrait.php index 157d2d85..7f2fd645 100644 --- a/src/Steps/Drupal/LanguageTrait.php +++ b/src/Steps/Drupal/LanguageTrait.php @@ -16,10 +16,11 @@ * - Add languages by their ISO code, skipping ones already installed. * * Languages created here are removed after the scenario along with every other - * entity the scenario created. A scenario that also installs the 'language' - * module leaves that removal to the module uninstall, with - * '@behat-steps-entity-cleanup-skip:language', because the two teardown hooks - * run in no guaranteed order. + * entity the scenario created. + * + * The 2 teardown hooks run in no guaranteed order. A scenario that also + * installs the 'language' module therefore leaves that removal to the module + * uninstall, with '@behat-steps-entity-cleanup-skip:language'. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ diff --git a/src/Steps/Drupal/RedirectTrait.php b/src/Steps/Drupal/RedirectTrait.php index aec0e819..094703e5 100644 --- a/src/Steps/Drupal/RedirectTrait.php +++ b/src/Steps/Drupal/RedirectTrait.php @@ -18,7 +18,7 @@ /** * Manage Drupal redirect entities provided by the contrib `redirect` module. * - * - Create one or more redirects from a table of source/destination/status. + * - Create 1 or more redirects from a table of source/destination/status. * - Delete redirects by source path. * - Assert that redirects do or do not exist for given source paths. * - Created redirects are automatically removed at the end of the scenario. @@ -37,7 +37,7 @@ trait RedirectTrait { protected static array $redirectAllowedStatusCodes = [301, 302, 303, 307, 308]; /** - * Create one or more redirects. + * Create 1 or more redirects. * * The `status_code` column is optional and defaults to `301` when omitted * or left blank. Allowed values: 301, 302, 303, 307, 308. @@ -86,7 +86,7 @@ public function redirectCreate(TableNode $table): void { /** * Delete redirects by source path. * - * Each row is one source path. Rows that match no existing redirect are + * Each row is 1 source path. Rows that match no existing redirect are * silently skipped. * * @code @@ -121,7 +121,7 @@ public function redirectDelete(TableNode $table): void { } /** - * Assert that one or more redirects exist. + * Assert that 1 or more redirects exist. * * The `from` column is required. The `to` and `status_code` columns are * optional: when blank or omitted, only the source path is matched. @@ -182,9 +182,9 @@ public function redirectAssertExist(TableNode $table): void { } /** - * Assert that no redirect exists for one or more source paths. + * Assert that no redirect exists for 1 or more source paths. * - * Each row is one source path. + * Each row is 1 source path. * * @code * Then the following redirects should not exist: diff --git a/src/Steps/Drupal/TaxonomyTrait.php b/src/Steps/Drupal/TaxonomyTrait.php index ed9b8aaa..48daf2a3 100644 --- a/src/Steps/Drupal/TaxonomyTrait.php +++ b/src/Steps/Drupal/TaxonomyTrait.php @@ -21,7 +21,7 @@ * Manage Drupal taxonomy terms with vocabulary organization. * * - Create term vocabulary structures using field values. - * - Navigate to term pages + * - Navigate to term pages. * - Verify vocabulary configurations. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext @@ -184,7 +184,7 @@ public function taxonomyAssertVocabularyNotExists(string $vocabulary): void { } /** - * Assert that a taxonomy term exist by name. + * Assert that a taxonomy term exists by name. * * @code * Then the taxonomy term "Apple" from the vocabulary "Fruits" should exist diff --git a/src/Steps/Drupal/UserTrait.php b/src/Steps/Drupal/UserTrait.php index 67505258..22679a2a 100644 --- a/src/Steps/Drupal/UserTrait.php +++ b/src/Steps/Drupal/UserTrait.php @@ -104,7 +104,7 @@ public function userCreateWithFields(TableNode $table): void { /** * Create users from a table of field values. * - * Each row becomes one user; each column is a base property or a field. A + * Each row becomes 1 user; each column is a base property or a field. A * `roles` column takes a comma-separated list, assigned after the account is * saved. A row without a `pass` column gets a random password. * @@ -566,7 +566,7 @@ public function userAssertNotBlocked(string $name): void { * Create a user carrying the roles and extra fields, and log in as them. * * @param string $roles - * One role, or several as a comma-separated list. + * A single role, or several as a comma-separated list. * @param array $extra_fields * Additional values to set on the account. * @@ -592,8 +592,8 @@ public function userCreateAndLogIn(string $roles, array $extra_fields = []): voi * @param \DrevOps\BehatSteps\Backend\Entity\EntityStubInterface $stub * The saved user stub. * @param string $roles - * One role, or several as a comma-separated list. An empty string assigns - * nothing. + * A single role, or several as a comma-separated list. An empty string + * assigns nothing. */ public function userAssignRoles(UserCapabilityInterface $backend, EntityStubInterface $stub, string $roles): void { foreach (array_filter(array_map(trim(...), explode(',', $roles))) as $role) { diff --git a/src/Steps/Drupal/WatchdogTrait.php b/src/Steps/Drupal/WatchdogTrait.php index 976a4f63..567eb8a9 100644 --- a/src/Steps/Drupal/WatchdogTrait.php +++ b/src/Steps/Drupal/WatchdogTrait.php @@ -125,7 +125,7 @@ public function watchdogAfterStep(AfterStepScope $scope): void { } /** - * Check for errors that the last step could not have seen. + * Check for errors the last-step check could not have read. * * A scenario whose earlier step failed never ran its last step, so nothing * was checked at step scope. A scenario that passed may still log an error @@ -178,8 +178,8 @@ public function watchdogAssertErrorsNotExist(string $context): void { /** * Read the errors logged since the scenario started, and clear them. * - * Read entries are deleted so a later check in the same scenario sees only - * new ones. + * Read entries are deleted so a later check in the same scenario returns + * only new ones. * * @return array * Rendered entries at or above the severity threshold, keyed by their @@ -206,7 +206,7 @@ public function watchdogReadErrors(): array { define('WATCHDOG_WARNING', 4); } - // Remove entries less severe than a warning. + // Entries less severe than a warning are removed. foreach ($entries as $key => $error) { if ($error->severity > WATCHDOG_WARNING) { unset($entries[$key]); diff --git a/src/Steps/Web/AccessibilityTrait.php b/src/Steps/Web/AccessibilityTrait.php index 534b73cf..584837f6 100644 --- a/src/Steps/Web/AccessibilityTrait.php +++ b/src/Steps/Web/AccessibilityTrait.php @@ -37,19 +37,21 @@ * - `@behat-steps-skip:AccessibilityTrait` Opt the scenario or feature out entirely. * * Tool-agnostic. Any engine that runs inside the existing Mink session can - * be plugged in by overriding `accessibilityRunEngine()` (perform the - * assessment, return raw results) and `accessibilityNormalizeResults()` - * (remap raw output into the canonical shape the rest of the trait expects). + * be plugged in by overriding `accessibilityRunEngine()` and + * `accessibilityNormalizeResults()`. The first performs the assessment and + * returns raw results; the second remaps raw output into the canonical shape + * the rest of the trait reads. * * Reporting. Each scenario writes its own HTML and JUnit report. After the * whole suite, a single cross-page `accessibility_report_.html` * (timestamp `YYYYMMDD_HHMMSS`) is written to the same directory, - * de-duplicating every assessed page and rolling violations up by rule. One - * file is written per run, so a run never overwrites a previous one. The + * de-duplicating every assessed page and rolling violations up by rule. + * + * 1 file is written per run, so a run never overwrites a previous one. The * aggregate accumulates in process-global state, so under parallel Behat each * process writes its own report. * - * Console output. A one-line per-page summary can be printed to the console + * Console output. A 1-line per-page summary can be printed to the console * as pages are assessed. Printing is off by default; set the * `BEHAT_ACCESSIBILITY_PRINT` environment variable to a non-empty value other * than `0`, or override `accessibilityGetPrintCli()`, to enable it. @@ -90,12 +92,13 @@ trait AccessibilityTrait { /** * Working directory captured before any test bootstrap can chdir(). * - * The default report directory anchors to this rather than a live - * `getcwd()` call. A Drupal bootstrap chdir()s to the docroot, so a live - * `getcwd()` would resolve the report directory outside the path-anchored - * location used by the rest of the run. Captured once at `@BeforeSuite`, - * before the first scenario, so it records the directory the run was - * launched from. + * The default report directory is resolved against this value rather than + * a live `getcwd()` call. A Drupal bootstrap chdir()s to the docroot, so a + * live `getcwd()` would resolve the report directory away from the rest of + * the run's paths. + * + * The value is captured once at `@BeforeSuite`, before the first scenario, + * so it records the directory the run was launched from. */ protected static ?string $accessibilityBaseDir = NULL; @@ -173,9 +176,6 @@ trait AccessibilityTrait { public static function accessibilityCaptureBaseDir(BeforeSuiteScope $scope): void { if (self::$accessibilityBaseDir === NULL) { $cwd = getcwd(); - // Leave the base unset when getcwd() fails so - // accessibilityGetReportDir() retries it later rather than locking in - // an empty, root-relative base. if ($cwd !== FALSE) { self::$accessibilityBaseDir = $cwd; } @@ -253,10 +253,10 @@ public function accessibilityAutoAssess(AfterStepScope $scope): void { $this->accessibilityAssess($this->accessibilityGetDefaultRules()); } - // A failed step has already failed the scenario, so gating on top of it - // would report a violation found on a page the step left incomplete. The - // gate is applied whether or not this step assessed a new page, because a - // last step that navigates nowhere still ends the scenario. + // A failed step has already failed the scenario, so gating it as well + // would report a violation from a page the step left incomplete. The gate + // runs whether or not this step assessed a new page, because a last step + // that does not navigate still ends the scenario. if (!$scope->getTestResult()->isPassed() || !$this->lastStepReached($scope)) { return; } @@ -268,10 +268,10 @@ public function accessibilityAutoAssess(AfterStepScope $scope): void { /** * Write the scenario reports, feed the suite aggregate, then gate if needed. * - * A step that fails on its own skips every step after it, including the one - * that would have applied the gate, so the gate is applied here instead. The - * scenario has already failed by then, so it cannot mask a passing scenario - * from the rerun cache. + * A failing step skips every step after it, including the one that applies + * the gate, so the gate is applied here instead. The scenario has already + * failed by then, so it cannot mask a passing scenario from the rerun + * cache. * * @throws \Behat\Mink\Exception\ExpectationException * If a violation at or above the threshold was collected. @@ -391,11 +391,11 @@ protected function accessibilityEnforceGate(): void { * Return the JavaScript source to inject into the page. * * Default: fetched once per process from accessibilityGetCdnUrl(). Override - * to ship the engine script from a vendored package or asset path. + * to return the engine script from a vendored package or asset path. * * A read is bounded by accessibilityGetFetchTimeout() and retried up to - * accessibilityGetFetchAttempts() times, so a stalled or throttled source - * costs a bounded wait per attempt instead of blocking until PHP's own + * accessibilityGetFetchAttempts() times. A stalled or throttled source + * blocks for at most the timeout per attempt, not until PHP's own * default_socket_timeout expires. */ public function accessibilityGetJs(): string { @@ -435,8 +435,10 @@ public function accessibilityGetJs(): string { * Default: a single read bounded by the given timeout, returning FALSE * when the read fails. An HTTP or HTTPS location is fetched through the * bare client, which carries no scenario state; any other location is read - * as a local file. Override to fetch through a different HTTP client; - * accessibilityGetJs() supplies the retries around it. + * as a local file. + * + * Override to fetch through a different HTTP client; accessibilityGetJs() + * supplies the retries around it. * * @param string $url * Location the engine source is read from. @@ -498,8 +500,9 @@ public function accessibilityGetCdnUrl(): string { * * A relative `report_dir` is resolved against the directory the run was * launched from. That base is captured at `@BeforeSuite`, so it is stable - * even after a Drupal bootstrap chdir()s to the docroot. When the suite hook - * has not run, the live working directory is used. + * even after a Drupal bootstrap chdir()s to the docroot. + * + * When the suite hook has not run, the live working directory is used. */ public function accessibilityGetReportDir(): string { $directory = $this->getOptionString('accessibility', 'report_dir'); @@ -519,7 +522,9 @@ public function accessibilityGetReportDir(): string { * The trait recognises this exact tag plus value variants * (`:critical`, `:serious`, `:moderate`, `:minor`, * `:warning`, `:strict`, `:any`) for per-scenario gate - * configuration. Default: `accessibility`. Override to shorten. + * configuration. + * + * Default: `accessibility`. Override to shorten. */ public function accessibilityGetAutoTag(): string { return $this->getOptionString('accessibility', 'auto_tag'); @@ -555,7 +560,7 @@ public function accessibilityGetFailOnIncomplete(): bool { } /** - * Return TRUE to print a one-line per-page summary to the console. + * Return TRUE to print a 1-line per-page summary to the console. * * Default: enabled only when the `BEHAT_ACCESSIBILITY_PRINT` environment * variable is set to a non-empty value other than `0`. Override to @@ -570,7 +575,7 @@ public function accessibilityGetPrintCli(): bool { /** * Return the canonical impact levels in descending severity order. * - * Default: the four `ACCESSIBILITY_IMPACT_*` constants on this trait. + * Default: the 4 `ACCESSIBILITY_IMPACT_*` constants on this trait. * Engines with a different severity vocabulary map to these constants * inside `accessibilityNormalizeResults()`. * @@ -601,9 +606,11 @@ protected static function accessibilityGetDefaultImpacts(): array { * * Default: injects `accessibilityGetJs()`, runs the engine with the * given rule identifier, returns the engine's native output. Override - * to call a different engine. The return value is fed to - * `accessibilityNormalizeResults()` before any other trait logic touches - * it, so the raw shape does not have to match the canonical shape. + * to call a different engine. + * + * The return value is passed to `accessibilityNormalizeResults()` before + * any other trait method reads it, so the raw shape does not have to match + * the canonical shape. * * @param string $rules * Engine-specific rule identifier. @@ -623,10 +630,9 @@ public function accessibilityRunEngine(string $rules): array { )); $session->wait(30000, 'window.__accessibilityResults !== null'); - // Serialize to a JSON string in the browser rather than returning the raw - // object: the result graph is large and nested, and some browser drivers - // (e.g. chrome-mink) cannot walk every property when marshalling a live - // object. + // Serialize to a JSON string in the browser. The result graph is large + // and nested, and some browser drivers (e.g. chrome-mink) cannot walk + // every property when marshalling a live object. $results = json_decode((string) $session->evaluateScript('return JSON.stringify(window.__accessibilityResults);'), TRUE); if (!is_array($results)) { @@ -647,14 +653,13 @@ public function accessibilityRunEngine(string $rules): array { * `['violations' => [...], 'incomplete' => [...], 'passes' => [...]]` * * Default: maps each finding into the canonical fields explicitly. The - * default engine's native shape happens to share field names with the - * canonical shape, so this default mostly copies values straight across. - * Each field is still named at the call site, so the method also serves - * as a template for overrides. + * default engine's native shape shares field names with the canonical + * shape, so this default mostly copies values straight across. * - * Override when wiring a different engine to map its native output (e.g. - * pa11y's `issues[]`, Lighthouse's `audits`) into the canonical - * structure. + * Each field is still named individually, so the method also serves as a + * template for overrides. Override when wiring a different engine to map + * its native output (e.g. pa11y's `issues[]`, Lighthouse's `audits`) into + * the canonical structure. * * @param array $raw * Raw result from `accessibilityRunEngine()`. @@ -883,14 +888,14 @@ protected static function accessibilityStringifyTarget(array $target): string { * * Default: strip the configured Mink `base_url` prefix so reports show the * page path (`/contact`) rather than the internal host and port - * (`http://nginx:8080/contact`). The absolute form is noise and makes - * reports non-portable. The base URL itself maps to `/` and the query - * string is kept. - * - * Only the known `base_url` is stripped: a genuinely cross-origin URL - * captured during assessment stays absolute, so it remains - * distinguishable. Override to keep the absolute URL or to format it - * differently. + * (`http://nginx:8080/contact`). The absolute form adds no information and + * makes reports non-portable. + * + * The base URL itself maps to `/` and the query string is kept. Only the + * known `base_url` is stripped: a cross-origin URL captured during + * assessment stays absolute, so it remains distinguishable. + * + * Override to keep the absolute URL or to format it differently. */ protected function accessibilityFormatUrl(string $url): string { $base = rtrim((string) $this->getMinkParameter('base_url'), '/'); @@ -925,8 +930,8 @@ protected static function accessibilityBlankUrls(): array { /** * Render the scenario-level HTML report from collected results. * - * Composes the page wrapper around the per-URL section markup. The two - * pieces are split so consumers can rebrand the page without touching + * Composes the page wrapper around the per-URL section markup. The 2 + * pieces are split so consumers can rebrand the page without changing * the section logic. */ protected function accessibilityRenderHtml(): string { @@ -938,8 +943,8 @@ protected function accessibilityRenderHtml(): string { * * Default: a self-contained HTML document with the trait's built-in * styles. Override to brand the report (custom doctype, header/footer, - * external stylesheet, project logo, etc.) without having to rebuild - * the section markup - the caller already supplies it as `$sections`. + * external stylesheet, project logo, etc.) without rebuilding the section + * markup, which `$sections` already holds. * * @param string $sections * Pre-rendered per-URL section markup from @@ -988,7 +993,7 @@ protected function accessibilityRenderHtmlPage(string $sections): string { } /** - * Render the per-URL section markup (one `
` per visited URL). + * Render the per-URL section markup (1 `
` per visited URL). * * Returns only the inner content that the page wrapper embeds. Override * to change how each section renders (rare); for branding the @@ -1061,7 +1066,7 @@ protected function accessibilityRenderIssueList(string $heading, string $css_cla * Violations are gated by the scenario's effective threshold, exactly as * the pass/fail gate is: only violations meeting the threshold are * serialised as `` cases. An advisory run (threshold `never`) - * therefore writes a report with zero failures instead of one that fails + * therefore writes a report with 0 failures instead of one that fails * a JUnit-consuming CI check. * * Violations below the threshold are recorded as passing cases carrying @@ -1133,9 +1138,9 @@ protected function accessibilityRenderJunit(): string { /** * Record the scenario's formatted results for the suite-level aggregate. * - * URLs are formatted here, in the instance phase, so the static renderer can - * reuse the one `accessibilityFormatUrl()` helper (and any consumer override - * of it) without an instance to call it on. + * The static renderer has no instance to call `accessibilityFormatUrl()` + * or a consumer override of it on, so URLs are formatted in the instance + * phase. * * @param string $dir * The resolved per-scenario report directory, captured for the static @@ -1166,12 +1171,14 @@ protected function accessibilityAggregateCapture(string $dir): void { } /** - * Write the aggregate report when at least one scenario produced results. + * Write the aggregate report when at least 1 scenario produced results. * - * One timestamped file is written per suite run, so a run never overwrites a - * previous one. The same timestamp drives the filename and the in-page - * "generated" line; it is resolved here so the render methods stay - * deterministic for tests. + * 1 timestamped file is written per suite run, so a run never overwrites a + * previous one. + * + * The same timestamp is used for the filename and the in-page "generated" + * line. It is resolved here so the render methods stay deterministic for + * tests. */ protected static function accessibilityWriteAggregateReport(): void { if (self::$accessibilityAggregate === []) { @@ -1208,7 +1215,7 @@ protected static function accessibilityAggregateFilename(int $time): string { * The accumulated per-scenario results. * * @return array> - * One entry per unique URL, in first-seen order, each holding its + * 1 entry per unique URL, in first-seen order, each holding its * violations, incomplete and passes counts, and visiting scenarios. */ protected static function accessibilityAggregatePages(array $aggregate): array { @@ -1317,12 +1324,11 @@ protected static function accessibilityAggregateRollup(array $pages, array $impa } /** - * Assemble every value the renderer needs into one data array. + * Assemble every value the renderer reads into 1 data array. * - * All calculation happens here and in the methods it calls - - * de-duplication, severity tallies, sorting, counting, and target - * flattening - so the single renderer only has to turn ready values into - * markup. + * De-duplication, severity tallies, sorting, counting, and target + * flattening all happen here and in the methods it calls. The single + * renderer only turns ready values into markup. * * @param array> $aggregate * The accumulated per-scenario results. @@ -1444,10 +1450,12 @@ protected static function accessibilityAggregateFindings(array $issues): array { /** * Render the entire aggregate report from prepared data. * - * This is the single rendering entry point. Every value it needs is already - * computed in `$data` by accessibilityAggregateData(), so a consumer can - * override this one method to completely restyle the report - markup, CSS, - * and layout - without touching any of the aggregation logic. + * This is the single rendering entry point. Every value it reads is already + * computed in `$data` by accessibilityAggregateData(). + * + * A consumer can therefore override this method alone to completely + * restyle the report - markup, CSS, and layout - without changing any of + * the aggregation logic. * * @param array $data * Render-ready data from accessibilityAggregateData(). diff --git a/src/Steps/Web/CommandTrait.php b/src/Steps/Web/CommandTrait.php index 395ca959..7d845a64 100644 --- a/src/Steps/Web/CommandTrait.php +++ b/src/Steps/Web/CommandTrait.php @@ -23,7 +23,8 @@ * * Commands run through the system shell with the privileges of the process * that runs the tests. The command string is passed to the shell verbatim and - * is subject to shell expansion, so never interpolate untrusted input into it. + * is subject to shell expansion, so untrusted input must never be + * interpolated into it. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ diff --git a/src/Steps/Web/CookieTrait.php b/src/Steps/Web/CookieTrait.php index e20cd8d8..fb80a1c0 100644 --- a/src/Steps/Web/CookieTrait.php +++ b/src/Steps/Web/CookieTrait.php @@ -271,8 +271,8 @@ public function cookieFindByName(string $name, bool $is_partial = FALSE): ?array public function cookieGetAll(): array { $cookies = $this->browserDriverFor(CookieCapabilityInterface::class)->cookieGetAll(); - // The capability reports wire-form values; an assertion compares against - // the value a step was written with. + // The capability returns URL-encoded values; a step compares against the + // literal written in the feature. foreach ($cookies as &$cookie) { $cookie['value'] = rawurldecode($cookie['value']); } diff --git a/src/Steps/Web/DateTrait.php b/src/Steps/Web/DateTrait.php index 5a161aab..b6ea1c98 100644 --- a/src/Steps/Web/DateTrait.php +++ b/src/Steps/Web/DateTrait.php @@ -27,10 +27,12 @@ * - `[relative:-1 day]` converted to `1893456000` * - `[relative:-1 day#Y-m-d]` converted to `2017-11-5` * - * `dateRelativeProcessValue()` is public API. It and its helpers are static so - * a token resolves without a context instance. Late static binding routes the - * resolution through a `dateGetNow()` override in the composing context, - * which is the supported seam for pinning the clock. + * `dateRelativeProcessValue()` is public API. It and its helpers are static, + * so a token resolves without a context instance. + * + * Late static binding routes the resolution through a `dateGetNow()` override + * in the composing context. That override is the supported way to hold the + * current time constant. * * Skip processing with tag: `@behat-steps-skip:DateTrait`. * @@ -112,9 +114,9 @@ public function dateRelativeTransformTable(TableNode $table): TableNode { * | test article | [relative:-1 day] | * @endcode * - * @note A formatted return value can land on a different day than the - * scenario expects when the offset crosses midnight, because an absent - * 'now' resolves to the current minute rather than a fixed time of day. + * @note An absent `$now` resolves to the current minute, not a fixed time of + * day. A formatted return value whose offset crosses midnight can then fall + * on a different day than the scenario expects. */ public static function dateRelativeProcessValue(string $value, ?int $now = NULL): string { if (!static::dateRelativeStringHasToken($value)) { diff --git a/src/Steps/Web/DiagnosticsTrait.php b/src/Steps/Web/DiagnosticsTrait.php index 08d38089..521f405d 100644 --- a/src/Steps/Web/DiagnosticsTrait.php +++ b/src/Steps/Web/DiagnosticsTrait.php @@ -14,9 +14,9 @@ /** * Append on-failure diagnostics to the failure message of any failed step. * - * When a step fails, the exception message alone is often not enough to - * diagnose a red CI run. This trait hooks every step and, only when the step - * failed, appends a compact diagnostics block to the failure message: + * The exception message of a failed step is often not enough to diagnose a + * CI failure. This trait hooks every step and, only when the step failed, + * appends a compact diagnostics block to the failure message: * * - `URL` - the current page URL. * - `HTTP status` - the last response status code. @@ -210,7 +210,7 @@ public function diagnosticsFindBrowserDriverName(): ?string { /** * Return collected JavaScript console error messages. * - * Two sources are merged and de-duplicated: the `JavascriptTrait` registry + * 2 sources are merged and de-duplicated: the `JavascriptTrait` registry * when the context also uses it, and the live browser buffer its collector * populates. The registry is detected at runtime, so there is no hard * dependency on that trait. Both are best-effort and yield nothing under a diff --git a/src/Steps/Web/DropzoneTrait.php b/src/Steps/Web/DropzoneTrait.php index f95bcb6f..a55da181 100644 --- a/src/Steps/Web/DropzoneTrait.php +++ b/src/Steps/Web/DropzoneTrait.php @@ -12,15 +12,16 @@ /** * Simulate a real multi-file drag-and-drop gesture onto a Dropzone target. * - * - Drop one or more files on a CSS-selected target in a single native event. + * - Drop 1 or more files on a CSS-selected target in a single native event. * - Fixture paths resolve against the Mink `files_path` parameter. * - Works on any element that handles native `drop` events (Dropzone.js, * custom drop targets, framework widgets). * * Mink's `attachFile` writes each file to a hidden `` * sequentially, so file A finishes uploading before file B starts. Real users - * release multiple files together, which fires a single `drop` event whose - * `dataTransfer.files` contains all of them and triggers concurrent uploads. + * release multiple files together, so a single `drop` event carries all of + * them in `dataTransfer.files` and triggers concurrent uploads. + * * Race conditions in dedup maps, status indicators, error handlers and * server-side queues reproduce only under the multi-file path. * @@ -45,9 +46,9 @@ public function dropzoneDropFile(string $path, string $selector): void { } /** - * Drop one or more files on the target element in a single native event. + * Drop 1 or more files on the target element in a single native event. * - * Provide one fixture path per row. + * Provide 1 fixture path per row. * * @code * When I drop the following files on the dropzone ".dropzone": diff --git a/src/Steps/Web/ElementTrait.php b/src/Steps/Web/ElementTrait.php index 1782b2f0..1edddba6 100644 --- a/src/Steps/Web/ElementTrait.php +++ b/src/Steps/Web/ElementTrait.php @@ -116,7 +116,7 @@ public function elementPressButtonByIndex(string $button, int $index): void { } /** - * When I trigger the JS event :event on the element :selector. + * Trigger a JS event on the element defined by the selector. * * @code * When I trigger the JS event "click" on the element "#submit-button" @@ -369,9 +369,10 @@ public function elementAssertNotExistsWithAttributeContainingValue(string $selec * Assert an element has a computed CSS property with a value. * * The value is compared against the value computed by the browser, not - * against the value written in the stylesheet: `color: red` computes to - * `rgb(255, 0, 0)` and `margin: 1em` computes to a pixel length. The - * property name is accepted in either `background-color` or + * against the value written in the stylesheet. `color: red` computes to + * `rgb(255, 0, 0)` and `margin: 1em` computes to a pixel length. + * + * The property name is accepted in either `background-color` or * `backgroundColor` form; CSS custom properties are used verbatim. The * assertion applies to the first element matching the selector. * @@ -434,16 +435,16 @@ public function elementAssertCssPropertyNotContains(string $selector, string $pr /** * Assert that one element stacks above another. * - * Compares the effective paint order rather than the `z-index` property: - * a `z-index` read from an element is only meaningful within its own - * stacking context, so a child of a stacking-context-forming ancestor can - * carry a high `z-index` and still paint below an element with a lower one. + * Compares the effective paint order rather than the `z-index` property, + * because a `z-index` is only meaningful within its own stacking context. + * A child of a stacking-context-forming ancestor can carry a high `z-index` + * and still paint below an element with a lower one. * * The comparison walks the stacking context chain of both elements, finds - * the context they share, and compares the two participants that branch off - * it, using document order to break a tie. Painting order within a single - * stacking context (floats, inline content and positioned descendants) is - * not modelled. + * the context they share, and compares the 2 participants that branch off + * it. Document order breaks a tie; painting order within a single stacking + * context (floats, inline content and positioned descendants) is not + * modelled. * * @code * Then the element "#modal" should stack above the element "#page-header" @@ -471,7 +472,7 @@ public function elementAssertStacksBelow(string $selector1, string $selector2): } /** - * Assert the element :selector should be at the top of the viewport. + * Assert that the element is at the top of the viewport. * * @code * Then the element "#header" should be at the top of the viewport @@ -486,7 +487,7 @@ public function elementAssertElementAtTopOfViewport(string $selector): void { } /** - * Assert the element :selector should be centered in the viewport. + * Assert that the element is centered in the viewport. * * Checks that the vertical center of the element is within the middle third * of the viewport. @@ -506,9 +507,10 @@ public function elementAssertElementCenteredInViewport(string $selector): void { /** * Assert that an element is pinned to the top of the viewport. * - * The element's top edge has to sit within 2 pixels of the viewport top, - * which absorbs the sub-pixel offsets that normal rendering produces. Use - * the step with an explicit tolerance for layouts that need a larger one. + * The element's top edge must be within 2 pixels of the viewport top; the + * tolerance covers the sub-pixel offsets that normal rendering produces. + * Use the step with an explicit tolerance for layouts that require a + * larger one. * * This asserts where the element currently renders, so scroll the page * first to tell a pinned element apart from one that starts at the top of @@ -984,10 +986,10 @@ protected function elementAssertStackingOrder(string $selector1, string $selecto * The CSS selector of the second element. * * @return string - * A pipe-delimited string of the order (`1` when the first element stacks - * above the second one, `-1` when it stacks below it, `0` when both - * selectors match the same element), the effective z-index of each - * compared participant, and the basis of the comparison. + * A pipe-delimited string of the order, the effective z-index of each + * compared participant, and the basis of the comparison. The order is `1` + * when the first element stacks above the second one, `-1` when it stacks + * below it, and `0` when both selectors match the same element. */ protected function elementResolveStackingOrder(string $selector1, string $selector2): string { $selector1_js = json_encode($selector1, JSON_UNESCAPED_SLASHES); @@ -1136,7 +1138,7 @@ protected function elementAssertPinnedToTopWithin(string $selector, int $toleran $script = 'var rect = {{ELEMENT}}.getBoundingClientRect(); return rect.top + "|" + rect.height;'; [$top, $height] = explode('|', (string) $this->elementExecuteJs($selector, $script), 2); - // An element that is not rendered reports a zero-sized box at the origin, + // An element that is not rendered reports a 0 by 0 box at the origin, // which would otherwise read as pinned. if ((float) $height <= 0) { if ($is_inverted) { diff --git a/src/Steps/Web/FieldTrait.php b/src/Steps/Web/FieldTrait.php index 61f80887..2e21e2cd 100644 --- a/src/Steps/Web/FieldTrait.php +++ b/src/Steps/Web/FieldTrait.php @@ -29,7 +29,7 @@ * - Assert field existence, state, and selected options. * - Support for specialized widgets like color pickers and rich text editors. * - Disable browser validation for forms with deferred execution. - * - Use @disable-form-validation tag to automatically disable validation for all forms. + * - The @disable-form-validation tag disables validation for all forms. * * Skip processing with tag: `@behat-steps-skip:FieldTrait` * @@ -141,9 +141,9 @@ public function fieldDisableFormBrowserValidation(string $selector): void { /** * Fill in a multi-value field widget with a list of values. * - * Locates the field wrapper by label, counts existing rows, clicks - * "Add another item" as many times as needed (waiting for AJAX between - * clicks), and fills each row in order. + * Locates the field wrapper by label and counts existing rows. "Add another + * item" is clicked as many times as needed, waiting for AJAX between clicks, + * and each row is filled in order. * * Requires a JavaScript-capable browser driver because the "Add another * item" button relies on AJAX. @@ -306,14 +306,12 @@ public function fieldFillWysiwyg(string $field, string $value): void { $parent_element = $element->getParent(); - // Support CKEditor 4. $is_ckeditor_4 = !empty($driver->find($parent_element->getXpath() . "/div[contains(@class,'cke')]")); if ($is_ckeditor_4) { $script = << @@ -937,9 +936,9 @@ public function fieldGetRequiredMarkerSelectors(): array { /** * Check if a given field element is marked as required. * - * Checks the configured marker selectors against the field, then the - * associated label, then a `*` character in the label text, then any - * descendant of the label matching a selector. + * Checks the configured marker selectors against the field, then against + * the associated label. Next, a `*` character in the label text is checked, + * then any descendant of the label matching a selector. */ public function fieldIsMarkedRequired(NodeElement $field_element): bool { $selectors = $this->fieldGetRequiredMarkerSelectors(); @@ -981,8 +980,8 @@ public function fieldIsMarkedRequired(NodeElement $field_element): bool { * Check whether an element itself carries one of the marker selectors. * * Mink can search within an element but cannot test the element against a - * selector, so the two selector shapes the markers use are read off the - * element directly. + * selector. The 2 selector shapes the markers use are read off the element + * directly instead. * * @param \Behat\Mink\Element\NodeElement $element * The element to test. @@ -1007,7 +1006,7 @@ protected function fieldMatchesMarker(NodeElement $element, array $selectors): b } /** - * The path of the current page, as used in failure messages. + * The path of the current page. * * @return string * The path component of the current URL. @@ -1035,7 +1034,7 @@ protected function fieldXpathLiteral(string $value): string { /** * Disable browser validation for forms. * - * Silently handles cases where forms don't exist yet (deferred execution). + * A selector that matches no form raises no error. * * @param string|null $selector * The CSS selector for form(s). If NULL, disables all forms on page. diff --git a/src/Steps/Web/FileDownloadTrait.php b/src/Steps/Web/FileDownloadTrait.php index ca46d50f..3a0e59bc 100644 --- a/src/Steps/Web/FileDownloadTrait.php +++ b/src/Steps/Web/FileDownloadTrait.php @@ -317,7 +317,6 @@ public function fileDownloadOpenZip(): \ZipArchive { throw new \RuntimeException('Downloaded file information does not have content type data.'); } // @codeCoverageIgnoreEnd - // A ".zip" file name is exempt from the content-type check. $file_name = $this->fileDownloadDownloadedFileInfo['file_name'] ?? ''; $has_zip_extension = str_ends_with(strtolower($file_name), '.zip'); @@ -346,9 +345,8 @@ public function fileDownloadOpenZip(): \ZipArchive { /** * Download file. * - * The request goes through the detached client, so it carries the - * scenario's cookies and headers and leaves the page the session holds - * untouched. + * The detached client sends the request, so it carries the scenario's + * cookies and headers. The page the session holds is left untouched. * * @param string $url * URL to download file from. diff --git a/src/Steps/Web/IframeTrait.php b/src/Steps/Web/IframeTrait.php index 11779c55..8cf6025c 100644 --- a/src/Steps/Web/IframeTrait.php +++ b/src/Steps/Web/IframeTrait.php @@ -32,8 +32,6 @@ trait IframeTrait { */ #[When('I switch to the iframe with the selector :selector')] public function iframeSwitchTo(string $selector): void { - // Switching frames and naming an unnamed one both need a real browser, so - // a browser driver that runs no JavaScript fails naming the capability. $this->browserDriverFor(JavascriptCapabilityInterface::class); $iframe = $this->getSession()->getPage()->find('css', $selector); diff --git a/src/Steps/Web/JavascriptTrait.php b/src/Steps/Web/JavascriptTrait.php index f31e5fe7..8054d214 100644 --- a/src/Steps/Web/JavascriptTrait.php +++ b/src/Steps/Web/JavascriptTrait.php @@ -140,8 +140,6 @@ public function javascriptBeforeStep(BeforeStepScope $scope): void { return; } - // Collection runs through the Mink script API, which is the same for every - // browser driver, so any JavaScript-capable one qualifies. // @codeCoverageIgnoreStart if (!$this->browserDriverHas(JavascriptCapabilityInterface::class)) { return; @@ -176,8 +174,6 @@ public function javascriptAfterStep(AfterStepScope $scope): void { return; } - // Collection runs through the Mink script API, which is the same for every - // browser driver, so any JavaScript-capable one qualifies. // @codeCoverageIgnoreStart if (!$this->browserDriverHas(JavascriptCapabilityInterface::class)) { return; @@ -201,8 +197,8 @@ public function javascriptAfterStep(AfterStepScope $scope): void { return; } - // Asserted outside the collection block above so the blanket catch cannot - // swallow the failure. + // The assertion runs outside the try block above, so the blanket catch + // cannot swallow the failure. $this->javascriptAsserted = TRUE; $this->javascriptAssertErrorsNotExist(); } @@ -294,7 +290,6 @@ protected function javascriptCollectFromPage(string $url): void { } // @codeCoverageIgnoreStart catch (\Exception) { - // Script evaluation can throw. } // @codeCoverageIgnoreEnd } diff --git a/src/Steps/Web/JsonTrait.php b/src/Steps/Web/JsonTrait.php index 0bbfe134..353edd52 100644 --- a/src/Steps/Web/JsonTrait.php +++ b/src/Steps/Web/JsonTrait.php @@ -339,7 +339,7 @@ public function jsonAssertPathFalse(string $path): void { * Assert that the array or object at a JSONPath has a number of elements. * * The path must resolve to a single array or object; its elements are then - * counted. Use a container path such as `$.items` rather than `$.items[*]`. + * counted. A container path such as `$.items` is required, not `$.items[*]`. * * @code * Then the JSON path "$.items" should have "3" elements diff --git a/src/Steps/Web/KeyboardTrait.php b/src/Steps/Web/KeyboardTrait.php index 735d4520..c74bc1a9 100644 --- a/src/Steps/Web/KeyboardTrait.php +++ b/src/Steps/Web/KeyboardTrait.php @@ -92,9 +92,8 @@ public function keyboardPressKeysOnElement(string $keys, ?string $selector): voi * If method is used for invalid browser driver. */ protected function keyboardPressKeyOnElementSingle(string $char, ?string $selector): void { - // Resolved before the key map is built so a browser driver that cannot - // dispatch a key event fails naming the capability rather than the ones - // that have it. + // Resolve the capability before the key map is built, so a browser + // driver that cannot dispatch a key event fails naming the capability. $keyboard = $this->browserDriverFor(KeyboardCapabilityInterface::class); $keys = [ @@ -133,13 +132,13 @@ protected function keyboardPressKeyOnElementSingle(string $char, ?string $select throw new \RuntimeException(sprintf('Unsupported key "%s" provided.', $char)); } - // Syn, the JS library that provides synthetic events, can tab only - // from an element that can receive focus. A tab press with no - // selector therefore targets a visually hidden, screen-reader - // compatible anchor injected as the first element inside . - // Triggering the key on the anchor moves focus to the first element - // in the tab order without focusing the anchor itself. + // Syn, the synthetic-events JS library, can tab only from a focusable + // element, so a tab press with no selector targets an injected anchor. if ($selector === NULL && $char === 'tab') { + // The anchor is visually hidden, screen-reader compatible and the + // first element inside . Triggering the key on it moves focus + // to the first element in the tab order without focusing the anchor + // itself. $selector = '#injected-focusable'; $script = << diff --git a/src/Steps/Web/MetatagTrait.php b/src/Steps/Web/MetatagTrait.php index 77a64f68..b95f7f30 100644 --- a/src/Steps/Web/MetatagTrait.php +++ b/src/Steps/Web/MetatagTrait.php @@ -131,9 +131,9 @@ public function metatagAssertNotContainsHtml(string $name): void { /** * Assert the canonical URL equals a value. * - * Both the actual and expected URLs are resolved to absolute form against - * the Mink base URL, so a relative expected value matches an absolute - * canonical href for the same page. + * The actual and expected URLs are both resolved to absolute form against + * the Mink base URL. A relative expected value therefore matches an + * absolute canonical href for the same page. * * @code * Then the canonical URL should be "https://example.com/about" @@ -256,7 +256,7 @@ public function metatagAssertRobotsNotContains(string $directive): void { /** * Assert hreflang alternates are valid. * - * Checks, without fetching any alternate page, that at least one hreflang + * Checks, without fetching any alternate page, that at least 1 hreflang * alternate exists and that a self-referencing alternate for the current * URL is present. Every hreflang value must be a well-formed language code * (or "x-default"). @@ -526,7 +526,7 @@ public function metatagIsValidHreflang(string $value): bool { * Fetch a URL through the detached client, leaving the page untouched. * * The request carries the scenario's cookies and headers, so an alternate - * page behind a login or basic auth is fetched as the scenario sees it. + * page behind a login or basic auth is fetched with the scenario's access. * * @param string $url * The absolute URL to fetch. diff --git a/src/Steps/Web/PathTrait.php b/src/Steps/Web/PathTrait.php index ece5118c..58ba2366 100644 --- a/src/Steps/Web/PathTrait.php +++ b/src/Steps/Web/PathTrait.php @@ -34,8 +34,8 @@ trait PathTrait { public function pathSetBasicAuth(string $username, string $password): void { $this->getSession()->setBasicAuth($username, $password); - // The browser driver keeps the credentials to itself, so the requests the - // library sends outside the session read them from the header bag. + // The browser driver does not expose the credentials, so requests the + // library sends outside the session read them from `$requestHeaders`. $this->requestHeadersSet('Authorization', 'Basic ' . base64_encode($username . ':' . $password)); } diff --git a/src/Steps/Web/RandomTrait.php b/src/Steps/Web/RandomTrait.php index 951ef08d..cead8732 100644 --- a/src/Steps/Web/RandomTrait.php +++ b/src/Steps/Web/RandomTrait.php @@ -17,11 +17,11 @@ * Replace random-value tokens in step arguments and table cells. * * - Resolve `[?:[,]]` tokens to generated values. - * - Return one value per token for the whole scenario. + * - Return 1 value per token for the whole scenario. * * Built-in types are `string`, `name`, `machine_name`, `int`, `email` and * `uuid`. The default is `string` with length `10`, so `[?title]`, - * `[?title:string]` and `[?title:string,10]` share one value. + * `[?title:string]` and `[?title:string,10]` share 1 value. * * Operates on Gherkin text alone: no Mink session and no backend, so the trait * works in any suite. @@ -35,21 +35,21 @@ trait RandomTrait { protected const RANDOM_BRACKET_REGEX = '#(\[\?[a-z0-9_]+(?::[^\]]+)?\])#i'; /** - * Token literal as it appears in the feature file -> canonical cache key. + * Maps each token literal in the feature file to its canonical cache key. * - * Parsing memo so the same literal does not get re-parsed on every - * transform invocation. + * The map caches the parsed key, so the same literal is not re-parsed on + * every transform invocation. * * @var array */ protected array $randomLiterals = []; /** - * Canonical cache key -> generated value. + * Maps each canonical cache key to its generated value. * - * Canonical key is 'name:type:arg1,arg2,...' with defaults applied, - * so '[?title]', '[?title:string]' and '[?title:string,10]' collapse - * to the same key. + * The canonical key is 'name:type:arg1,arg2,...' with defaults applied, + * so '[?title]', '[?title:string]' and '[?title:string,10]' share the + * same key. * * @var array */ @@ -69,16 +69,13 @@ trait RandomTrait { * Pre-resolves every token literal found in the current scenario. * * Every literal is cached before the first step runs, so repeated token - * literals stay stable. This holds even when the first occurrence is inside - * a step argument that Behat dispatches before the rest are visited. + * literals stay stable. */ #[BeforeScenario] public function randomBeforeScenario(BeforeScenarioScope $scope): void { $this->randomValues = []; $this->randomLiterals = []; - // A transform receives no scope, so the decision is made here and the - // transforms read the result. $this->randomEnabled = !$this->skipTag(__TRAIT__, $scope); if (!$this->randomEnabled) { @@ -244,7 +241,7 @@ protected function randomNormalizeArgs(string $type, array $args): array { } /** - * Validates length-style args (one optional non-negative integer). + * Validates length-style args (1 optional non-negative integer). * * @param string $type * The generator type, used for error messages. @@ -271,7 +268,7 @@ protected function randomNormalizeLengthArgs(string $type, array $args): array { } /** - * Validates 'int' args (zero args for full range, or two integer bounds). + * Validates 'int' args (0 args for full range, or 2 integer bounds). * * @param list $args * Raw args parsed from the token literal. @@ -320,7 +317,7 @@ protected function randomNormalizeArglessArgs(string $type, array $args): array * Dispatches to the type-specific generator. * * 'randomNormalizeArgs()' has already validated the args, so the casts are - * safe and not a fallback. + * safe. * * @param string $type * The generator type extracted from the token. diff --git a/src/Steps/Web/ResponsiveTrait.php b/src/Steps/Web/ResponsiveTrait.php index bd636827..511a997a 100644 --- a/src/Steps/Web/ResponsiveTrait.php +++ b/src/Steps/Web/ResponsiveTrait.php @@ -307,7 +307,7 @@ public function responsiveGetAllBreakpoints(): array { * The breakpoint name, or NULL when the node carries no @breakpoint tag. * * @throws \RuntimeException - * If the node carries more than one. + * If the node carries more than 1. */ protected function responsiveFindTagBreakpoint(TaggedNodeInterface $node, string $node_type): ?string { $breakpoints = Tag::values($node, self::RESPONSIVE_BREAKPOINT_TAG); @@ -396,8 +396,7 @@ public function responsiveResize(int $width, int $height): void { } // @codeCoverageIgnoreStart catch (\Exception) { - // A browser driver without resize support throws; the exception is - // ignored. + // A browser driver without resize support throws. } // @codeCoverageIgnoreEnd } diff --git a/src/Steps/Web/WaitTrait.php b/src/Steps/Web/WaitTrait.php index a927cdec..fc6c5bbe 100644 --- a/src/Steps/Web/WaitTrait.php +++ b/src/Steps/Web/WaitTrait.php @@ -57,9 +57,10 @@ public function waitBeforeScenario(BeforeScenarioScope $scope): void { /** * Wait for AJAX before a step that navigates or submits. * - * The after-step wait fires only on steps matching the same pattern. AJAX - * from a non-matching step, such as a select or keystroke bound to a Drupal - * behaviour, is still in flight at the next click. This hook settles it. + * The after-step wait fires only on steps matching the pattern, so AJAX + * from a non-matching step is still in flight at the next click. A select + * or keystroke bound to a Drupal behaviour is one such step, so this hook + * settles the AJAX first. */ #[BeforeStep] public function waitBeforeStep(BeforeStepScope $scope): void { diff --git a/src/Steps/Web/XmlTrait.php b/src/Steps/Web/XmlTrait.php index 0fd6e2c1..26690a2a 100644 --- a/src/Steps/Web/XmlTrait.php +++ b/src/Steps/Web/XmlTrait.php @@ -565,9 +565,9 @@ public function xmlAssertValidRssFeed(): void { /** * Assert that the response is a valid Atom feed. * - * Checks the required Atom structure: a `feed` root in the Atom namespace - * with `id`, `title` and `updated`, and each `entry` with `id`, `title` - * and `updated`. + * Checks the required Atom structure. The `feed` root is in the Atom + * namespace with `id`, `title` and `updated`, and each `entry` has `id`, + * `title` and `updated`. * * @code * Then the response should be a valid Atom feed @@ -589,7 +589,7 @@ protected function xmlResolveContent(): string { } /** - * Parse XML content without disturbing the cached document. + * Parse XML content without altering the cached document. * * @param string $content * The XML content to parse. @@ -786,12 +786,11 @@ public function xmlValidateRelaxNg(string $schema): void { * Validate the response against a DTD. * * The DTD is embedded as an internal subset and the response is reloaded - * with validation enabled, so a DTD from a file and an inline DTD share this - * code path. + * with validation enabled. * * External references are not resolved during validation, so a `SYSTEM` - * entity declared in the DTD is loaded from neither a local path nor the - * network. Validation fails with a resolver error if a DTD references one. + * entity in the DTD is loaded from neither a local path nor the network. + * Validation fails with a resolver error if a DTD references one. * * DTDs are namespace-unaware, so a namespaced response is validated verbatim * and its `xmlns` attributes must be declared in the DTD. This matches @@ -822,10 +821,9 @@ public function xmlValidateDtd(string $dtd): void { $document = new \DOMDocument(); libxml_clear_errors(); - // A SYSTEM entity declared in the DTD is dereferenced while validating. - // The loader returns NULL for every external reference, so validation can - // neither read a local path nor reach the network. LIBXML_NONET is passed - // as well, but on its own it blocks only the network half. + // A SYSTEM entity declared in the DTD is dereferenced while validating, so + // the loader returns NULL for every external reference. LIBXML_NONET is + // passed as well, but on its own it blocks only the network half. $previous_loader = function_exists('libxml_get_external_entity_loader') ? libxml_get_external_entity_loader() : NULL; libxml_set_external_entity_loader(static fn(): null => NULL); diff --git a/tests/behat/bootstrap/BehatCliTrait.php b/tests/behat/bootstrap/BehatCliTrait.php index 3fe8dcbe..af6ff227 100644 --- a/tests/behat/bootstrap/BehatCliTrait.php +++ b/tests/behat/bootstrap/BehatCliTrait.php @@ -124,19 +124,16 @@ public function behatCliWriteFeatureContextFile(array $traits = []): string { ]; // Navigation and session steps appear in nearly every generated scenario - // as setup for the trait under test, so the baseline carries them. A - // baseline trait that is itself under test is composed once. + // as setup for the trait under test, so the baseline carries them. $qualified_traits = []; foreach (array_merge(static::BEHAT_CLI_BASELINE_TRAITS, $traits) as $trait) { - // A tag names the trait's context and short name, as in - // 'Drupal\ModuleTrait'. A tag with no context names a web trait. $qualified_traits[] = str_contains((string) $trait, '\\') ? $trait : 'Web\\' . $trait; } foreach (array_diff(array_unique($qualified_traits), static::BEHAT_CLI_INHERENT_TRAITS) as $qualified) { - // Two contexts can hold the same short name, so each import carries a - // context-qualified alias and one tag can name both. + // 2 contexts can hold the same short name, so each import carries a + // context-qualified alias and 1 tag can name both. $alias = str_replace('\\', '_', (string) $qualified); // A 'Helper\' tag names a trait outside the vocabulary subtree. $root = str_starts_with((string) $qualified, 'Helper\\') ? 'DrevOps\\BehatSteps\\' : 'DrevOps\\BehatSteps\\Steps\\'; @@ -414,8 +411,7 @@ public function behatCliAssertFailWithError(PyStringNode $message): void { #[Then('it should fail with an exception:')] public function behatCliAssertFailWithException(PyStringNode $message): void { $this->itShouldPassOrFailWith('fail', $message); - // A non-assertion failure is a \RuntimeException. An assertion failure - // is an assertion exception. + // A non-assertion failure is a \RuntimeException. if (!str_contains($this->getOutput(), ' (RuntimeException)')) { throw new \RuntimeException('The output does not contain an "(RuntimeException)" string as expected.'); } diff --git a/tests/behat/bootstrap/FeatureContext.php b/tests/behat/bootstrap/FeatureContext.php index 42f090ce..bcd934e1 100644 --- a/tests/behat/bootstrap/FeatureContext.php +++ b/tests/behat/bootstrap/FeatureContext.php @@ -25,9 +25,9 @@ class FeatureContext extends DrupalContext { /** * Override dateGetNow() method to return a preset value for testing. * - * The override sits on the class, not in FeatureContextTrait: the generated - * trait-tag context composes that trait beside the trait under test, and two - * traits declaring the same method collide. + * The override is declared on the class, not in FeatureContextTrait. The + * generated trait-tag context composes that trait beside the trait under + * test, and 2 traits declaring the same method collide. */ public static function dateGetNow(): int { return strtotime('2024-07-15 12:00:00'); @@ -36,25 +36,25 @@ public static function dateGetNow(): int { /** * Override elementGetScrollIntoViewCenter() to allow runtime toggling. * - * The override sits on the class, not in FeatureContextTrait: the generated - * trait-tag context composes that trait beside the trait under test, and two - * traits declaring the same method collide. + * The override is declared on the class, not in FeatureContextTrait. The + * generated trait-tag context composes that trait beside the trait under + * test, and 2 traits declaring the same method collide. */ protected function elementGetScrollIntoViewCenter(): bool { return $this->testElementScrollCenter; } /** - * Override accessibilityGetReportDir() to anchor reports to the base path. + * Override accessibilityGetReportDir() to place reports under the base path. * * Behat is launched from the build directory but configured with the * project-root behat.php, so the captured working directory is not the * base path. Deriving the base from the Mink files_path keeps accessibility * reports in the same .logs tree as the other Behat artifacts. * - * The override sits on the class, not in FeatureContextTrait: the generated - * trait-tag context composes that trait beside the trait under test, and two - * traits declaring the same method collide. + * The override is declared on the class, not in FeatureContextTrait. The + * generated trait-tag context composes that trait beside the trait under + * test, and 2 traits declaring the same method collide. */ public function accessibilityGetReportDir(): string { return dirname((string) $this->getMinkParameter('files_path'), 3) . '/.logs/test_results/accessibility'; diff --git a/tests/behat/bootstrap/FeatureContextTrait.php b/tests/behat/bootstrap/FeatureContextTrait.php index 2b5fa518..5eab3acb 100644 --- a/tests/behat/bootstrap/FeatureContextTrait.php +++ b/tests/behat/bootstrap/FeatureContextTrait.php @@ -101,7 +101,6 @@ public function testSetCookie(string $name, string $value): void { $driver = $session->getDriver(); - // WebDriver-based drivers like Selenium2Driver. if (method_exists($driver, 'getWebDriverSession')) { $driver->getWebDriverSession()->setCookie([ 'name' => $name, @@ -110,17 +109,16 @@ public function testSetCookie(string $name, string $value): void { ]); } - // BrowserKit-based drivers like GoutteDriver. if (method_exists($driver, 'getClient')) { $cookie_jar = $driver->getClient()->getCookieJar(); $cookie = new Cookie($name, rawurlencode($value)); $cookie_jar->set($cookie); } - // CDP-based drivers like the Chrome (chrome-mink) driver. Their own - // setCookie() binds the cookie to the configured base URL, so a page served - // from another origin never receives it. Writing through the document keeps - // the cookie on the origin the scenario is on. + // A CDP-based driver like Chrome (chrome-mink) binds setCookie() cookies + // to the configured base URL, so pages on another origin never receive + // them. Writing through the document keeps the cookie on the origin the + // scenario is on. if (method_exists($driver, 'getCookies')) { $driver->evaluateScript(sprintf('document.cookie = %s;', json_encode($name . '=' . rawurlencode($value) . '; path=/'))); } @@ -199,8 +197,6 @@ protected function testGetAllCookies(): array { $cookie_list[$cookie['name']] = $cookie['value']; } } - - // CDP-based drivers like the Chrome (chrome-mink) driver. elseif (method_exists($driver, 'getCookies')) { foreach ($driver->getCookies() as $cookie) { $cookie_list[$cookie['name']] = rawurldecode((string) $cookie['value']); diff --git a/tests/phpunit/src/ContextCompositionTest.php b/tests/phpunit/src/ContextCompositionTest.php index 5d4a2173..77fa4244 100644 --- a/tests/phpunit/src/ContextCompositionTest.php +++ b/tests/phpunit/src/ContextCompositionTest.php @@ -39,9 +39,6 @@ class ContextCompositionTest extends UnitTestCase { */ protected const CHAIN = [WebRawContext::class, WebContext::class, DrupalContext::class]; - /** - * Assert that each context extends the one above it. - */ public function testTheChainIsLinear(): void { $parents = []; @@ -120,9 +117,6 @@ public function testTheChainComposesEachTraitOnce(): void { $this->assertSame([], $repeated); } - /** - * Assert that the root context composes the web helpers and no step trait. - */ public function testTheRootContextComposesTheWebHelpersOnly(): void { $expected = [LastStepTrait::class, RequestHeadersTrait::class, StringTrait::class]; $composed = static::composedTraits(WebRawContext::class, 'Helper'); @@ -131,9 +125,6 @@ public function testTheRootContextComposesTheWebHelpersOnly(): void { $this->assertSame([], static::composedTraits(WebRawContext::class, 'Steps'), sprintf('%s registers no steps of its own.', WebRawContext::class)); } - /** - * Assert that the Drupal context declares the user-manager contract. - */ public function testTheDrupalContextIsUserAware(): void { $this->assertContains(UserAwareInterface::class, class_implements(DrupalContext::class)); } @@ -218,8 +209,8 @@ protected static function composedMembers(\ReflectionClass $reflection): array { /** * Assert that a helper trait composed twice holds one slot of state. * - * A step trait composes the helper it needs and the root context composes - * it too, so both read and write the same state rather than a copy each. + * A step trait and the root context compose the same helper, so they read + * and write the same state rather than a copy each. */ public function testHelperComposedTwiceSharesItsState(): void { $context = new HelperStateSubject(); diff --git a/tests/phpunit/src/DataProviderConventionTest.php b/tests/phpunit/src/DataProviderConventionTest.php index 82c15a46..dc4582ac 100644 --- a/tests/phpunit/src/DataProviderConventionTest.php +++ b/tests/phpunit/src/DataProviderConventionTest.php @@ -13,7 +13,9 @@ * * A provider is named after the test it serves, declared after that test, and * declares the return type its body produces. Which of the 2 forms a class - * uses is left to the class. CONTRIBUTING.md states the rules. + * uses is left to the class. + * + * CONTRIBUTING.md states the rules. */ #[CoversNothing] class DataProviderConventionTest extends UnitTestCase { diff --git a/tests/phpunit/src/DocsTest.php b/tests/phpunit/src/DocsTest.php index 30d32815..d4fe474d 100644 --- a/tests/phpunit/src/DocsTest.php +++ b/tests/phpunit/src/DocsTest.php @@ -89,9 +89,9 @@ protected function setUp(): void { require_once __DIR__ . '/../../../docs.php'; - // The fixture traits are loaded up front so they are available to - // eval(). Drupal context traits are excluded because they are loaded - // from the test's temporary directory to get the correct context. + // The Web fixture traits are loaded up front so they are available to + // eval(). The Drupal fixture traits are loaded from the test's temporary + // directory instead, so they resolve to the correct context. $fixture_files = glob($this->getFixturesDir() . '/Web/*.php'); if ($fixture_files !== FALSE) { foreach ($fixture_files as $fixture_file) { @@ -2008,9 +2008,6 @@ public static function dataProviderReplaceContent(): array { ]; } - /** - * Test the extract_info function with actual reflection. - */ #[DataProvider('dataProviderExtractInfo')] public function testExtractInfo( array $trait_names, @@ -2054,9 +2051,6 @@ public static function dataProviderExtractInfo(): array { ]; } - /** - * Get the fixtures directory path. - */ protected function getFixturesDir(): string { return __DIR__ . '/../fixtures/docs'; } @@ -2153,9 +2147,6 @@ protected function setupExtractInfoTest(array $trait_names, ?string $context = N ]; } - /** - * Create a test context class that uses specified traits. - */ protected function createTestContext(array $trait_names, string $class_name = 'TestContextForDocs'): string { if (!class_exists($class_name, FALSE)) { $namespaced_traits = array_map(function ($trait_name): string { @@ -2172,7 +2163,7 @@ protected function createTestContext(array $trait_names, string $class_name = 'T } /** - * Test extract_info with trait having multiple methods (tests sorting). + * Tests that extract_info() sorts the methods of a multi-method trait. */ public function testExtractInfoMultipleMethods(): void { $trait_name = 'MultiMethodTrait'; @@ -2228,9 +2219,6 @@ public function testExtractInfoMissingTrait(): void { extract_info([$class_name], [], $paths['base_path']); } - /** - * Test extract_info with subdirectory traits (Drupal context). - */ public function testExtractInfoWithSubdirectory(): void { $trait_name = 'DrupalTrait'; $setup = $this->setupExtractInfoTest([$trait_name], 'Drupal'); @@ -2244,9 +2232,6 @@ public function testExtractInfoWithSubdirectory(): void { $this->assertSame('Drupal\\' . $trait_name, $result[$trait_name]['name_contextual']); } - /** - * Test extract_info with trait without matching methods. - */ public function testExtractInfoNoMatchingMethods(): void { $trait_name = 'NoMatchTrait'; $setup = $this->setupExtractInfoTest([$trait_name]); @@ -2261,9 +2246,6 @@ public function testExtractInfoNoMatchingMethods(): void { $this->assertEmpty($result[$trait_name]['methods']); } - /** - * Test extract_info validation of class comments. - */ #[DataProvider('dataProviderExtractInfoErrors')] public function testExtractInfoErrors(string $error_case, string $expected_error): void { $this->assertTrue( @@ -2273,9 +2255,6 @@ public function testExtractInfoErrors(string $error_case, string $expected_error ); } - /** - * Data provider for testExtractInfoErrors. - */ public static function dataProviderExtractInfoErrors(): array { return [ 'empty class comment' => [ @@ -2687,8 +2666,6 @@ public function testTagRegistry(): void { $this->assertSame('parametrized', $registry['accessibility']['form']); $this->assertSame('flag', $registry['disable-form-validation']['form']); - // Every entry is classified as exactly one of the two known forms and - // carries the description the reference renders. foreach ($registry as $prefix => $definition) { $this->assertIsString($prefix); $this->assertContains($definition['form'], ['parametrized', 'flag']); @@ -3009,8 +2986,8 @@ public function testExtractHelpers(): void { $this->assertSame('Sample trait carrying helpers for testing.', $trait['description']); // Steps, hooks, transformations, protected members, internal members and - // members belonging to another trait by name are all left out, and the - // rest is sorted by name. + // members whose name belongs to another trait are left out. The rest is + // sorted by name. $this->assertSame(['helperSampleBuild', 'helperSampleDefaults'], array_column($trait['helpers'], 'name')); $this->assertSame('public function helperSampleBuild(string $name, ?int $count = NULL, bool $strict = TRUE): string', $trait['helpers'][0]['signature']); @@ -3418,7 +3395,7 @@ public function testValidateEnvVars(): void { file_put_contents($base_path . '/docs/configuration.md', 'The `BEHAT_STEPS_DOCUMENTED` and `BEHAT_STEPS_DISABLE_CLEANUP` variables are documented.'); file_put_contents($base_path . '/src/Documented.php', ' 0100644, 'size' => strlen(static::$contents)]; } diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreAuthenticationMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreAuthenticationMethodsKernelTest.php index f43a9b84..4ba290b2 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreAuthenticationMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreAuthenticationMethodsKernelTest.php @@ -40,7 +40,7 @@ protected function setUp(): void { $this->installEntitySchema('user'); $this->installConfig(['user']); - // Anonymous user (uid 0) - Drupal expects it to exist. + // Drupal requires the anonymous user (uid 0) to exist. User::create([ 'uid' => 0, 'name' => '', @@ -50,9 +50,6 @@ protected function setUp(): void { $this->core = new Core($this->root); } - /** - * Tests that 'login()' switches the active account. - */ public function testLoginSwitchesAccount(): void { $account = User::create([ 'name' => 'alice', diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreBlockMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreBlockMethodsKernelTest.php index 2f12449c..9bd6b7f2 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreBlockMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreBlockMethodsKernelTest.php @@ -17,7 +17,7 @@ /** * Kernel tests for the block capability methods on Core. * - * Covers all four methods of 'BlockCapabilityInterface': + * Covers all 4 methods of 'BlockCapabilityInterface': * - 'blockPlace()' / 'blockDelete()' round-trip a 'block' config entity * (placement in a region of a theme). * - 'blockContentCreate()' / 'blockContentDelete()' round-trip a @@ -86,9 +86,6 @@ public function testBlockPlaceAndDeleteRoundTrip(): void { $this->assertNull(Block::load('test_powered_by')); } - /** - * Tests that 'blockPlace()' auto-generates an id when the stub omits it. - */ public function testBlockPlaceGeneratesIdWhenAbsent(): void { $stub = new EntityStub('block', NULL, [ 'plugin' => 'system_powered_by_block', @@ -105,9 +102,6 @@ public function testBlockPlaceGeneratesIdWhenAbsent(): void { $this->assertNotNull(Block::load($placement->id())); } - /** - * Tests that 'blockDelete()' uses the saved-entity slot when present. - */ public function testBlockDeleteUsesSavedEntity(): void { $stub = new EntityStub('block', NULL, [ 'id' => 'test_via_entity', @@ -122,9 +116,6 @@ public function testBlockDeleteUsesSavedEntity(): void { $this->assertNull(Block::load('test_via_entity')); } - /** - * Tests that 'blockDelete()' fails loudly when the stub has no id. - */ public function testBlockDeleteRequiresIdOnStub(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches('/id/'); @@ -132,9 +123,6 @@ public function testBlockDeleteRequiresIdOnStub(): void { $this->core->blockDelete(new EntityStub('block', NULL, ['plugin' => 'system_powered_by_block'])); } - /** - * Tests that 'blockContentCreate()' creates a content-block entity. - */ public function testBlockContentCreateAndDeleteRoundTrip(): void { BlockContentType::create(['id' => 'basic', 'label' => 'Basic'])->save(); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreCacheMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreCacheMethodsKernelTest.php index fcd02939..150c8c56 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreCacheMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreCacheMethodsKernelTest.php @@ -41,9 +41,6 @@ protected function setUp(): void { $this->core = new Core($this->root); } - /** - * Tests that 'cacheClear()' dispatches without error. - */ public function testCacheClearDispatches(): void { // Populate a cache entry so the clear has something to flush. \Drupal::cache()->set('drupal_backend_test:sentinel', 'value'); @@ -54,9 +51,6 @@ public function testCacheClearDispatches(): void { $this->assertFalse(\Drupal::cache()->get('drupal_backend_test:sentinel')); } - /** - * Tests that 'cacheClearStatic()' resets Drupal's static caches. - */ public function testCacheClearStaticResetsStatics(): void { $counter = &drupal_static('drupal_backend_test_counter'); $counter = 7; @@ -67,9 +61,6 @@ public function testCacheClearStaticResetsStatics(): void { $this->assertNull(drupal_static('drupal_backend_test_counter')); } - /** - * Tests that 'cacheClearStatic()' empties the memory cache bin. - */ public function testCacheClearStaticEmptiesTheMemoryBin(): void { \Drupal::cache('memory')->set('drupal_backend_test:memory', 'value'); $this->assertNotFalse(\Drupal::cache('memory')->get('drupal_backend_test:memory')); @@ -79,9 +70,6 @@ public function testCacheClearStaticEmptiesTheMemoryBin(): void { $this->assertFalse(\Drupal::cache('memory')->get('drupal_backend_test:memory')); } - /** - * Tests that 'getExtensionPathList()' includes the enabled system module. - */ public function testGetExtensionPathListIncludesEnabledModules(): void { $paths = $this->core->getExtensionPathList(); @@ -92,9 +80,6 @@ public function testGetExtensionPathListIncludesEnabledModules(): void { ); } - /** - * Tests that 'getRandom()' returns the random generator. - */ public function testGetRandomReturnsInjectedGenerator(): void { $random = new Random(); $core = new Core($this->root, 'default', $random); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreConfigMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreConfigMethodsKernelTest.php index 4c7a3917..29e01487 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreConfigMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreConfigMethodsKernelTest.php @@ -40,9 +40,6 @@ protected function setUp(): void { $this->core = new Core($this->root); } - /** - * Tests configSet writes and configGet reads the same value back. - */ public function testConfigSetAndGetRoundTrip(): void { $this->core->configSet('system.site', 'name', 'DrupalBackend Test Site'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreEntityCreateCommerceKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreEntityCreateCommerceKernelTest.php index aca5bdd5..99eccdd0 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreEntityCreateCommerceKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreEntityCreateCommerceKernelTest.php @@ -19,14 +19,6 @@ * A stub sets 'commerce_product.variations', a base entity_reference field * targeting 'commerce_product_variation'. The backend must resolve each * referenced variation and attach it to the product on save. - * - * Without the base-field auto-detection in 'expandEntityFields()', variations - * are filtered out of the field-handler pipeline and reach entity storage in - * raw scalar form. The product is then saved with no variations attached. - * - * Both the variation and the product are created via 'Core::entityCreate()'. - * The product is then loaded back via the entity type manager to assert the - * resolved relationship. */ #[CoversClass(Core::class)] #[Group('core')] diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreEntityMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreEntityMethodsKernelTest.php index 1150b7f0..c6a01996 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreEntityMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreEntityMethodsKernelTest.php @@ -87,9 +87,10 @@ public function testEntityCreateAndDeleteWithStub(): void { * * 'name' is a base field on the user entity type. Base fields are not * registered field storage configs, so the handler pipeline reaches them - * only through auto-detection. DefaultHandler then wraps the scalar value - * into the array form the field API expects, which is observable on the - * stub after create. + * only through auto-detection. + * + * DefaultHandler wraps the scalar value into the array form the field API + * expects, so the stub holds that array after create. */ public function testEntityCreateAutoExpandsBaseFieldsSetOnStub(): void { $stub = new EntityStub('user', NULL, [ @@ -103,9 +104,6 @@ public function testEntityCreateAutoExpandsBaseFieldsSetOnStub(): void { $this->assertSame([['value' => 'uma']], $stub->getValue('name'), 'base field "name" was routed through the handler pipeline.'); } - /** - * Tests 'entityDelete()' rejects a stub missing the resolved id key. - */ public function testEntityDeleteRejectsStubMissingIdKey(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches('/stub without the id key "uid" set/'); @@ -118,8 +116,7 @@ public function testEntityDeleteRejectsStubMissingIdKey(): void { * * 'user.roles' is a base entity_reference field targeting the user_role * config entity type. A stub sets it by label or id, and the backend must - * resolve and attach the reference. This test pins the end-to-end - * round-trip: stub -> backend -> storage -> reload -> assertion. + * resolve and attach the reference. */ public function testEntityCreateExpandsBaseEntityReferenceFieldOnStub(): void { Role::create(['id' => 'editor', 'label' => 'Editor'])->save(); @@ -138,9 +135,6 @@ public function testEntityCreateExpandsBaseEntityReferenceFieldOnStub(): void { $this->assertContains('editor', $account->getRoles(), 'entityCreate routed user.roles through EntityReferenceHandler for base-field expansion.'); } - /** - * Tests 'entityDelete()' uses the saved-entity slot when present. - */ public function testEntityDeleteUsesSavedEntity(): void { $entity = User::create([ 'name' => 'taylor', @@ -178,9 +172,9 @@ public function testEntityCreatePromotesTypedBundle(): void { * Tests 'entityCreate()' rejects an unknown entity type with a clear message. * * Drupal's 'EntityTypeManager::getDefinition()' raises a - * 'PluginNotFoundException' with plugin-system vocabulary that does not - * describe what a scenario author actually did wrong. The backend wraps it - * as a 'RuntimeException' that names the offending entity type. + * 'PluginNotFoundException' whose message uses plugin-system terms and does + * not describe the scenario author's mistake. The backend wraps it as a + * 'RuntimeException' that names the offending entity type. */ public function testEntityCreateRejectsUnknownEntityType(): void { $this->expectException(\RuntimeException::class); @@ -189,9 +183,6 @@ public function testEntityCreateRejectsUnknownEntityType(): void { $this->core->entityCreate(new EntityStub('nonexistent_type', NULL, ['name' => 'foo'])); } - /** - * Tests 'entityDelete()' rejects an unknown entity type with a clear message. - */ public function testEntityDeleteRejectsUnknownEntityType(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches('/Unknown entity type "nonexistent_type"/'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreMailMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreMailMethodsKernelTest.php index 5e3ac6bc..93ed0a5d 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreMailMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreMailMethodsKernelTest.php @@ -74,9 +74,6 @@ public function testMailLifecycleRoundTrip(): void { $this->assertSame([], $this->core->mailGet()); } - /** - * Tests that 'mailSend()' carries attachments through to the collected mail. - */ public function testMailSendCarriesAttachments(): void { $this->core->mailStartCollecting(); @@ -111,9 +108,6 @@ public function testMailSendWithEmptyAttachmentsOmitsKey(): void { /** * Tests mail collection swaps mailsystem senders when the module is on. - * - * Exercises 'replaceMailSenders()', 'startCollectingSystemMail()', and - * 'stopCollectingSystemMail()'. */ public function testMailCollectionRedirectsMailsystemSenders(): void { \Drupal::service('module_installer')->install(['mailsystem']); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreNodeMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreNodeMethodsKernelTest.php index e7ed4530..c60892b8 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreNodeMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreNodeMethodsKernelTest.php @@ -19,8 +19,8 @@ * Kernel test for node-related methods on Core via the backend. * * Exercises Core::nodeCreate and Core::nodeDelete end-to-end: bundle - * validation, optional 'author' → 'uid' remapping, expandEntityFields - * (no fields attached here, so it's a noop), save, and delete. + * validation, the optional 'author' to 'uid' remapping, expandEntityFields + * (a no-op here, with no fields attached), save, and delete. */ #[CoversClass(Core::class)] #[Group('core')] @@ -88,9 +88,6 @@ public function testNodeLifecycle(): void { $this->assertNull(Node::load($result->getValue('nid'))); } - /** - * Tests that nodeCreate rejects an unknown bundle. - */ public function testNodeCreateRejectsUnknownBundle(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Cannot create content because provided content type bogus does not exist.'); @@ -98,9 +95,6 @@ public function testNodeCreateRejectsUnknownBundle(): void { $this->core->nodeCreate(new EntityStub('node', 'bogus', ['title' => 'Nope'])); } - /** - * Tests that nodeCreate rejects a node with no type. - */ public function testNodeCreateRejectsMissingType(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage("Cannot create content because it is missing the required property 'type'."); @@ -108,9 +102,6 @@ public function testNodeCreateRejectsMissingType(): void { $this->core->nodeCreate(new EntityStub('node', NULL, ['title' => 'Nope'])); } - /** - * Tests that nodeCreate rejects an unknown 'author' value. - */ public function testNodeCreateRejectsUnknownAuthor(): void { $this->expectException(CreationAliasResolutionException::class); $this->expectExceptionMessageMatches("/user 'auther'.*does not exist/"); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreSystemMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreSystemMethodsKernelTest.php index feffc33a..b0f7ffc3 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreSystemMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreSystemMethodsKernelTest.php @@ -73,9 +73,6 @@ public function testModuleInstallAndUninstall(): void { $this->assertFalse(\Drupal::moduleHandler()->moduleExists('syslog'), 'moduleUninstall disabled syslog.'); } - /** - * Tests that getModuleList exposes enabled modules. - */ public function testGetModuleListIncludesEnabledModules(): void { $modules = $this->core->getModuleList(); @@ -101,9 +98,6 @@ public function testLanguageLifecycle(): void { $this->assertNull(ConfigurableLanguage::load('fr')); } - /** - * Tests that languageCreate returns FALSE when the language already exists. - */ public function testLanguageCreateReturnsFalseWhenLanguageExists(): void { $this->core->languageCreate(new EntityStub('language', NULL, ['langcode' => 'fr'])); @@ -112,9 +106,6 @@ public function testLanguageCreateReturnsFalseWhenLanguageExists(): void { $this->assertFalse($second); } - /** - * Tests that 'languageDelete()' throws when the language does not exist. - */ public function testLanguageDeleteThrowsWhenLanguageMissing(): void { $this->assertNull(ConfigurableLanguage::load('fr')); diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreTermMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreTermMethodsKernelTest.php index f23e73e6..f85f0f3c 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreTermMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreTermMethodsKernelTest.php @@ -82,18 +82,12 @@ public function testTermLifecycle(): void { $this->assertNull(Term::load($result->getValue('tid'))); } - /** - * Tests that termDelete returns FALSE for a non-existent term. - */ public function testTermDeleteReturnsFalseForMissingTerm(): void { $missing = new EntityStub('taxonomy_term', 'tags', ['tid' => 99999]); $this->assertFalse($this->core->termDelete($missing)); } - /** - * Tests that termCreate rejects a stub missing the vocabulary. - */ public function testTermCreateRejectsMissingVocabularyProperty(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches("/vocabulary is missing/"); @@ -101,9 +95,6 @@ public function testTermCreateRejectsMissingVocabularyProperty(): void { $this->core->termCreate(new EntityStub('taxonomy_term', NULL, ['name' => 'Orphan'])); } - /** - * Tests that termCreate rejects an unknown vocabulary. - */ public function testTermCreateRejectsUnknownVocabulary(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches("/vocabulary 'ghosts' does not exist/"); @@ -113,9 +104,6 @@ public function testTermCreateRejectsUnknownVocabulary(): void { ])); } - /** - * Tests that termCreate rejects a parent term that does not exist. - */ public function testTermCreateRejectsUnknownParent(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches("/parent term 'Missing' does not exist in vocabulary 'tags'/"); @@ -129,8 +117,8 @@ public function testTermCreateRejectsUnknownParent(): void { /** * Tests that 'vocabulary_machine_name' on a stub selects the vocabulary. * - * Existing happy-path coverage relies on the bundle constructor arg; this - * test pins the alternative path that uses the alias as a stub value. + * The happy-path test passes the vocabulary as the bundle constructor + * argument; this test passes it as the 'vocabulary_machine_name' stub value. */ public function testTermCreateWithVocabularyMachineNameAlias(): void { $stub = new EntityStub('taxonomy_term', NULL, [ diff --git a/tests/phpunit/src/Kernel/Backend/Core/CoreUserMethodsKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/CoreUserMethodsKernelTest.php index c0d97ee8..056a78cc 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/CoreUserMethodsKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/CoreUserMethodsKernelTest.php @@ -54,8 +54,8 @@ protected function setUp(): void { parent::setUp(); $this->installEntitySchema('user'); - // users_data is used by user_cancel's batch callback; required for - // synchronous userDelete to complete without hitting a missing table. + // users_data is used by user_cancel's batch callback; without it a + // synchronous userDelete fails with a missing-table error. $this->installSchema('user', ['users_data']); $this->installConfig(['user']); @@ -65,7 +65,7 @@ protected function setUp(): void { } /** - * Tests the full user/role lifecycle in one bundled method. + * Tests the full user/role lifecycle in 1 bundled method. */ public function testUserLifecycle(): void { $user_stub = new EntityStub('user', NULL, [ @@ -101,9 +101,6 @@ public function testUserLifecycle(): void { $this->assertNull(Role::load($role_id)); } - /** - * Tests that 'userAddRole()' throws when the role name is unknown. - */ public function testUserAddRoleThrowsOnUnknownRole(): void { $stub = new EntityStub('user', NULL, [ 'name' => 'ghost', @@ -118,9 +115,6 @@ public function testUserAddRoleThrowsOnUnknownRole(): void { $this->core->userAddRole($stub, 'nonexistent-role'); } - /** - * Tests that 'userAddRole()' throws when the stub's uid matches no account. - */ public function testUserAddRoleThrowsOnUnknownUser(): void { $role_id = $this->core->roleCreate(['access user profiles']); @@ -130,9 +124,6 @@ public function testUserAddRoleThrowsOnUnknownUser(): void { $this->core->userAddRole(new EntityStub('user', NULL, ['uid' => 999999]), $role_id); } - /** - * Tests that roleCreate rejects unknown permission strings. - */ public function testRoleCreateRejectsUnknownPermission(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Invalid permission "definitely not a real permission"'); @@ -141,7 +132,7 @@ public function testRoleCreateRejectsUnknownPermission(): void { } /** - * Tests that clearing caches forgets the permission list read earlier. + * Tests that clearing caches discards the permission list read earlier. * * @param string $method * The cache clearing method to call. @@ -163,9 +154,6 @@ public static function dataProviderClearingCachesForgetsThePermissionList(): \It yield 'every cache' => ['cacheClear']; } - /** - * Tests that installing a module forgets the permission list read earlier. - */ public function testModuleInstallForgetsThePermissionList(): void { $this->core->roleCreate(['access user profiles']); @@ -176,9 +164,6 @@ public function testModuleInstallForgetsThePermissionList(): void { $this->assertTrue($role->hasPermission('administer blocks')); } - /** - * Tests that uninstalling a module forgets the permission list read earlier. - */ public function testModuleUninstallForgetsThePermissionList(): void { $this->core->moduleInstall('block'); $this->core->roleCreate(['access user profiles']); @@ -191,9 +176,6 @@ public function testModuleUninstallForgetsThePermissionList(): void { $this->core->roleCreate(['administer blocks']); } - /** - * Tests 'roleCreate()' honours explicit id and label arguments. - */ public function testRoleCreateAcceptsExplicitIdAndLabel(): void { $role_id = $this->core->roleCreate(['access user profiles'], 'editor', 'Editor'); @@ -204,9 +186,6 @@ public function testRoleCreateAcceptsExplicitIdAndLabel(): void { $this->assertTrue($role->hasPermission('access user profiles')); } - /** - * Tests 'roleCreate()' falls back to the id as label when only id is given. - */ public function testRoleCreateFallsBackToIdAsLabel(): void { $role_id = $this->core->roleCreate([], 'content_editor'); @@ -216,9 +195,6 @@ public function testRoleCreateFallsBackToIdAsLabel(): void { $this->assertSame('content_editor', $role->label()); } - /** - * Tests that 'userCreate()' honours the 'roles' creation alias. - */ public function testUserCreateAppliesRolesAlias(): void { $role_id = $this->core->roleCreate(['access user profiles'], 'editor'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/AbstractHandlerFieldNotFoundKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/AbstractHandlerFieldNotFoundKernelTest.php index 8e9819b8..6f13c6df 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/AbstractHandlerFieldNotFoundKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/AbstractHandlerFieldNotFoundKernelTest.php @@ -14,11 +14,10 @@ /** * Kernel test for AbstractHandler's field-not-found guard. * - * 'Core::getFieldHandler()' already validates field existence before - * instantiating a handler, so the guard is only reachable when a caller - * constructs a handler directly (e.g. via custom Core subclasses). This test - * exercises that direct-construction path against a real entity_field.manager - * service. + * 'Core::getFieldHandler()' validates field existence before instantiating a + * handler, so the guard is reachable only when a caller constructs a handler + * directly (e.g. via custom Core subclasses). This test exercises that + * direct-construction path against a real entity_field.manager service. */ #[CoversClass(AbstractHandler::class)] #[Group('fields')] @@ -32,9 +31,6 @@ class AbstractHandlerFieldNotFoundKernelTest extends FieldHandlerKernelTestBase */ protected static $modules = self::BASE_MODULES; - /** - * Tests that the constructor throws when the requested field does not exist. - */ public function testConstructorThrowsOnUnknownField(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessageMatches('/does not exist on entity type "entity_test"/'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/AddressHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/AddressHandlerKernelTest.php index d4acd65f..e6d8fe4d 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/AddressHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/AddressHandlerKernelTest.php @@ -32,9 +32,6 @@ class AddressHandlerKernelTest extends FieldHandlerKernelTestBase { 'address', ]; - /** - * Tests round-trip for an address field with associative input. - */ public function testAddressAssociativeRoundTrip(): void { $this->attachAddressField(); @@ -54,9 +51,9 @@ public function testAddressAssociativeRoundTrip(): void { /** * Tests numeric-indexed input with country_code defaulted from field config. * - * Exercises AddressHandler's positional-to-keyed normalisation and its - * fallback where an omitted country_code falls back to the first entry in - * the field's available_countries list. + * Exercises AddressHandler's positional-to-keyed normalisation and the + * fallback that fills an omitted country_code from the first entry in the + * field's available_countries list. */ public function testAddressNumericInputFallsBackToAvailableCountry(): void { $this->attachAddressField(); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/BooleanHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/BooleanHandlerKernelTest.php index 7d764f3c..dab304b9 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/BooleanHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/BooleanHandlerKernelTest.php @@ -63,9 +63,6 @@ public function testFieldOnLabelResolvesToTrue(): void { $this->assertFieldRoundTripViaBackend('field_flag', ['Published']); } - /** - * Tests the field's configured off_label resolves to 0. - */ public function testFieldOffLabelResolvesToFalse(): void { $this->attachField('field_flag', 'boolean', [], [ 'on_label' => 'Published', @@ -75,9 +72,6 @@ public function testFieldOffLabelResolvesToFalse(): void { $this->assertFieldRoundTripViaBackend('field_flag', ['Draft']); } - /** - * Tests an unrecognised value raises a descriptive exception. - */ public function testUnrecognizedValueThrows(): void { $this->attachField('field_flag', 'boolean'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/CustomCoreKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/CustomCoreKernelTest.php index f85e2db7..5b7192c3 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/CustomCoreKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/CustomCoreKernelTest.php @@ -17,10 +17,10 @@ /** * Kernel test for consumer-supplied Core and its bundled field handlers. * - * Exercises the two extension seams advertised in the README against a full - * Drupal kernel. The test replaces 'Core' with 'ConsumerCore' - a fixture - * living outside the 'DrevOps\BehatSteps\Backend' namespace - and proves that - * its 'Field/' directory scan contributes handlers that actually run during + * Exercises the 2 extension seams documented in the README against a full + * Drupal kernel. The test replaces 'Core' with 'ConsumerCore', a fixture + * outside the 'DrevOps\BehatSteps\Backend' namespace, and proves that its + * 'Field/' directory scan contributes handlers that run during * 'entityCreate': * * - 'ConsumerProject\Backend\Field\TextLongHandler' takes 'text_long' over @@ -60,9 +60,9 @@ protected function setUp(): void { /** * Tests that the consumer override replaces the library's 'text_long'. * - * Input differs from the handler's marker so the assertion only passes - * when the consumer handler actually ran. A pass-through handler would - * leave the raw input in storage and fail the comparison. + * The input differs from the handler's marker, so the assertion passes only + * when the consumer handler ran. A pass-through handler would leave the raw + * input in storage and fail the comparison. */ public function testConsumerCoreOverridesLibraryHandler(): void { $this->attachField('field_body', 'text_long'); @@ -88,10 +88,11 @@ public function testConsumerCoreOverridesLibraryHandler(): void { * Tests that the consumer Core registers handlers for new field types. * * 'string_long' is a Drupal-core field type; the library does not ship a - * dedicated handler for it. Without 'ConsumerCore', the lookup would fall - * through to 'DefaultHandler' and store the raw input verbatim. The - * fixture adds a handler that rewrites 'value', and this test proves the - * rewritten value is what lands in storage. + * dedicated handler for it. Without 'ConsumerCore', the lookup falls through + * to 'DefaultHandler' and stores the raw input verbatim. + * + * The fixture adds a handler that rewrites 'value', and this test proves the + * rewritten value reaches storage. */ public function testConsumerCoreAddsHandlerForNewFieldType(): void { $this->attachField('field_summary', 'string_long'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/CustomModuleFieldKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/CustomModuleFieldKernelTest.php index 60f81b8a..a55a4a0a 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/CustomModuleFieldKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/CustomModuleFieldKernelTest.php @@ -13,17 +13,16 @@ /** * Kernel test: a contrib module's custom field types through the real backend. * - * The 'backend_field_test' fixture module ships two field types the backend has + * The 'backend_field_test' fixture module ships 2 field types the backend has * no handler for, standing in for any contrib module that introduces its own * field type. Both cases run against the real 'Core' with every built-in - * handler registered - not a stripped-down subclass - so this proves the - * classifier gate end to end: + * handler registered, so the classifier gate is exercised end to end: * * - 'backend_test_scalar' (plain-scalar columns) is handled by * 'DefaultHandler' and round-trips through real storage intact. - * - 'backend_test_reference' (an entity-reference target column) is refused at - * handler resolution with the actionable "register a dedicated handler" - * exception, rather than persisting a bogus id. + * - 'backend_test_reference' (an entity-reference target column) is refused + * at handler resolution with the "register a dedicated handler" exception + * instead of persisting an invalid id. */ #[CoversClass(Core::class)] #[Group('core')] @@ -52,9 +51,6 @@ public function testScalarFieldWithoutHandlerRoundTrips(): void { ]); } - /** - * Tests a custom entity-reference field with no handler is refused. - */ public function testReferenceFieldWithoutHandlerIsRejected(): void { $this->attachField('field_ref', 'backend_test_reference'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/DateRecurHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/DateRecurHandlerKernelTest.php index 5479c448..202537c7 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/DateRecurHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/DateRecurHandlerKernelTest.php @@ -12,13 +12,14 @@ /** * Kernel round-trip test for DateRecurHandler via the Core backend. * - * The 'date_recur' field type (provided by drupal/date_recur) stores five + * The 'date_recur' field type (provided by drupal/date_recur) stores 5 * columns ('value', 'end_value', 'rrule', 'timezone', 'infinite'), which * DefaultHandler cannot marshal. This test proves the backend resolves * DateRecurHandler for type 'date_recur' and that the multi-column storage - * accepts what the handler emits. The 'infinite' column is derived from the - * rrule by the field type's preSave(), so it is left out of the asserted - * round-trip. + * accepts what the handler emits. + * + * The 'infinite' column is derived from the rrule by the field type's + * preSave(), so it is left out of the asserted round-trip. */ #[CoversClass(DateRecurHandler::class)] #[Group('fields')] diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/DatetimeHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/DatetimeHandlerKernelTest.php index fe5e3f7e..af0b370d 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/DatetimeHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/DatetimeHandlerKernelTest.php @@ -14,10 +14,9 @@ /** * Kernel round-trip test for datetime fields via the Core backend. * - * Unit tests already cover DatetimeHandler::expand() math. This test adds - * the integration proof: Core::entityCreate resolves DatetimeHandler through - * its lookup chain, the handler's output is accepted by real datetime field - * storage, and the stored value round-trips unchanged. + * Core::entityCreate resolves DatetimeHandler through its lookup chain, real + * datetime field storage accepts the handler's output, and the stored value + * round-trips unchanged. */ #[CoversClass(DatetimeHandler::class)] #[Group('fields')] @@ -49,9 +48,6 @@ public function testDatetimeRoundTrip(): void { ]); } - /** - * Tests round-trip for a date-only field. - */ public function testDateOnlyRoundTrip(): void { $this->attachField('field_birthday', 'datetime', [ 'datetime_type' => DateTimeItem::DATETIME_TYPE_DATE, @@ -75,8 +71,8 @@ public function testRelativePrefixIsResolved(): void { ]); $this->core->entityCreate($stub); - // The 'relative:' prefix is stripped before parsing; the resulting value - // matches the same storage string the plain timestamp would have produced. + // The 'relative:' prefix is stripped before parsing, so the stored value + // equals the one a plain timestamp produces. $this->assertSame([['value' => '2026-01-02T03:04:05']], $stub->getValue('field_seen')); } diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/DefaultHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/DefaultHandlerKernelTest.php index 39ea7217..a0d296fd 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/DefaultHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/DefaultHandlerKernelTest.php @@ -13,10 +13,8 @@ * Kernel round-trip test for DefaultHandler via the Core backend. * * DefaultHandler is the fallback used for any field type without a dedicated - * handler class. This test verifies the fallback resolves correctly and that - * the passthrough output round-trips through real storage. The 'string' field - * type has no DrupalBackend handler, so the lookup chain lands on - * DefaultHandler. + * handler class. The 'string' field type has no DrupalBackend handler, so the + * lookup chain resolves to DefaultHandler. */ #[CoversClass(DefaultHandler::class)] #[Group('fields')] @@ -30,9 +28,6 @@ class DefaultHandlerKernelTest extends FieldHandlerKernelTestBase { */ protected static $modules = self::BASE_MODULES; - /** - * Tests round-trip for a string field (no specific handler defined). - */ public function testStringRoundTripViaDefaultHandler(): void { $this->attachField('field_note', 'string'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerEdgeCasesKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerEdgeCasesKernelTest.php index c8029049..fa12e64e 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerEdgeCasesKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerEdgeCasesKernelTest.php @@ -52,7 +52,6 @@ public function testTargetBundlesRestrictsMatches(): void { Vocabulary::create(['vid' => 'tags', 'name' => 'Tags'])->save(); Vocabulary::create(['vid' => 'categories', 'name' => 'Categories'])->save(); - // Matching term in the allowed bundle. Term::create(['name' => 'drupal', 'vid' => 'tags'])->save(); // Same-named term in a disallowed bundle should be ignored by the query. Term::create(['name' => 'drupal', 'vid' => 'categories'])->save(); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerKernelTest.php index 2ce6a28d..ad830220 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/EntityReferenceHandlerKernelTest.php @@ -46,9 +46,6 @@ protected function setUp(): void { $this->installEntitySchema('taxonomy_term'); } - /** - * Tests round-trip for an entity_reference field targeting users by name. - */ public function testUserReferenceByNameRoundTrip(): void { $this->attachField('field_owner', 'entity_reference', [ 'target_type' => 'user', @@ -62,9 +59,6 @@ public function testUserReferenceByNameRoundTrip(): void { $this->assertFieldRoundTripViaBackend('field_owner', ['alice']); } - /** - * Tests round-trip when the value is already a numeric id. - */ public function testUserReferenceByIdRoundTrip(): void { $this->attachField('field_owner', 'entity_reference', [ 'target_type' => 'user', @@ -81,9 +75,11 @@ public function testUserReferenceByIdRoundTrip(): void { * * A delta may use the field-item shape of file, image or * entity_reference_revisions values, e.g. '['target_id' => 'alice', - * 'display' => 1]'. The handler treats the main property value as the - * lookup label and resolves it to an id. The original array shape is - * preserved so any extra item properties round-trip through to storage. + * 'display' => 1]'. + * + * The handler treats the main property value as the lookup label and + * resolves it to an id. The original array shape is preserved so any extra + * item properties round-trip through to storage. */ public function testUserReferenceResolvesAssociativeArrayDelta(): void { $this->attachField('field_owner', 'entity_reference', [ @@ -95,12 +91,9 @@ public function testUserReferenceResolvesAssociativeArrayDelta(): void { $this->assertFieldRoundTripViaBackend('field_owner', [['target_id' => 'alice']]); } - /** - * Tests round-trip when deltas mix scalar labels and associative arrays. - */ public function testUserReferenceResolvesMixedScalarAndAssociativeDeltas(): void { - // Needs an unlimited-cardinality field to store two deltas; attachField() - // always creates a single-value field, so configure the storage inline. + // Needs an unlimited-cardinality field to store 2 deltas; attachField() + // always creates a single-value field, so the storage is configured inline. FieldStorageConfig::create([ 'field_name' => 'field_owners', 'entity_type' => self::ENTITY_TYPE, @@ -128,9 +121,7 @@ public function testUserReferenceResolvesMixedScalarAndAssociativeDeltas(): void * * Taxonomy terms are referenced through 'entity_reference' with * 'target_type = taxonomy_term', so the backend routes through - * EntityReferenceHandler. Covered here alongside the other - * EntityReferenceHandler targets rather than in its own suite because it is - * the same handler exercising a different 'target_type'. + * EntityReferenceHandler. */ public function testTaxonomyTermReferenceByNameRoundTrip(): void { Vocabulary::create(['vid' => 'tags', 'name' => 'Tags'])->save(); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerKernelTestBase.php b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerKernelTestBase.php index 917ee290..6da0b30e 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerKernelTestBase.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerKernelTestBase.php @@ -23,10 +23,10 @@ * 1. Call attachField() to declare the field under test. * 2. Call assertFieldRoundTripViaBackend() with the input value. * - * The round-trip assertion compares the backend-mutated EntityStub (which - * holds whatever the handler emitted from expand()) against the reloaded - * entity. No assertions are made against expect-specific expand() values; - * that coverage belongs in per-handler unit tests. + * The round-trip assertion compares the backend-mutated EntityStub, which + * holds the handler's expand() output, against the reloaded entity. It does + * not assert specific expand() values; that coverage belongs in the + * per-handler unit tests. */ #[RunTestsInSeparateProcesses] abstract class FieldHandlerKernelTestBase extends KernelTestBase { @@ -120,9 +120,11 @@ protected function attachField(string $field_name, string $type, array $storage_ * asserts the reloaded entity holds the same data. * * For single-property scalar values, the assertion compares against the - * main field column. For multi-property arrays (e.g. link.uri / link.title), - * the assertion compares only the keys the test set, ignoring computed or - * defaulted columns that the storage layer may populate. + * main field column. + * + * For multi-property arrays (e.g. link.uri / link.title), the assertion + * compares only the keys the test set. Computed or defaulted columns that + * the storage layer may populate are ignored. * * @param string $field_name * The field to round-trip. @@ -145,7 +147,7 @@ protected function assertFieldRoundTripViaBackend(string $field_name, array $val // Some handlers (e.g. ImageHandler) emit a flat associative array as // single-delta shorthand rather than a list of deltas. Normalise that - // shape into a one-element list so the iteration below is uniform. + // shape into a 1-element list so the iteration below is uniform. $expanded = $stub->getValue($field_name); $deltas = is_array($expanded) && !array_is_list($expanded) ? [$expanded] diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerRegistryKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerRegistryKernelTest.php index 14b73ead..032b56e1 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerRegistryKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldHandlerRegistryKernelTest.php @@ -15,10 +15,9 @@ /** * Kernel test asserting a consumer-registered handler wins end-to-end. * - * The unit tests cover registry semantics in isolation. This test proves - * that a class registered via 'Core::registerFieldHandler()' is the one - * instantiated when 'entityCreate()' expands a field. The stored value is - * observed to differ from what the fallback handler would produce. + * This test proves that a class registered via 'Core::registerFieldHandler()' + * is the one instantiated when 'entityCreate()' expands a field. The stored + * value is observed to differ from what the fallback handler would produce. */ #[CoversClass(Core::class)] #[Group('core')] @@ -54,11 +53,10 @@ protected function setUp(): void { /** * Tests that a consumer-registered handler replaces the fallback. * - * The input value is deliberately distinct from the handler's marker so - * the assertion can only pass when the consumer handler actually ran. - * 'DefaultHandler', which serves 'text_with_summary' when nothing is - * registered, would leave the raw input in storage and the comparison - * against 'MARKER' would fail. + * The input value differs from the handler's marker, so the assertion + * passes only when the consumer handler ran. 'DefaultHandler', which serves + * 'text_with_summary' when nothing is registered, would leave the raw input + * in storage and fail the comparison against 'MARKER'. */ public function testConsumerRegisteredHandlerWinsOverFallback(): void { $this->core->registerFieldHandler('text_with_summary', MarkerTextWithSummaryHandler::class); @@ -86,9 +84,9 @@ public function testConsumerRegisteredHandlerWinsOverFallback(): void { /** * Test-only handler that emits a deterministic marker value. * - * Extends 'AbstractHandler' directly so its class lineage does not include - * 'DefaultHandler', which proves the registry is the resolution path rather - * than a class-name convention. + * Extends 'AbstractHandler' directly so its lineage excludes 'DefaultHandler'. + * A resolution to this class can then only come from the registry, not from a + * class-name convention. */ class MarkerTextWithSummaryHandler extends AbstractHandler { diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldTypeCoverageKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldTypeCoverageKernelTest.php index 379c30bf..d85255c2 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/FieldTypeCoverageKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/FieldTypeCoverageKernelTest.php @@ -24,9 +24,9 @@ * (c) documented in the SKIP map with a reason (computed, write-only, or * otherwise not stub-expansion-compatible). * - * If a type falls into none of these buckets the test fails with the type - * name. The failure forces the contributor to add a handler, confirm - * DefaultHandler is safe, or record a SKIP entry with a reason. + * A type in none of these categories fails the test with its name. The + * contributor then adds a handler, confirms DefaultHandler is safe, or + * records a SKIP entry with a reason. */ #[CoversClass(Core::class)] #[Group('fields')] @@ -72,9 +72,6 @@ class FieldTypeCoverageKernelTest extends FieldHandlerKernelTestBase { 'shape_required' => 'Test-only fixture field type shipped by entity_test; not present in production Drupal installs.', ]; - /** - * Tests that every known field type has a coverage strategy. - */ public function testEveryKnownFieldTypeIsHandledOrSkipped(): void { $definitions = \Drupal::service('plugin.manager.field.field_type')->getDefinitions(); @@ -134,9 +131,9 @@ protected function isDefaultHandlerSafe(string $type): bool { } catch (\Throwable) { // Property construction fails for types that require settings not - // supplied here (e.g. entity_reference without target_type). Those are - // treated as unsafe: the classifier cannot reason about the properties, - // so the type needs a dedicated handler. + // supplied here (e.g. entity_reference without target_type). Those count + // as unsafe: the classifier cannot inspect the properties, so the type + // needs a dedicated handler. return FALSE; } @@ -146,10 +143,9 @@ protected function isDefaultHandlerSafe(string $type): bool { /** * Guards against DefaultHandler being mistakenly matched as a "handler". * - * The handler registry never lists DefaultHandler explicitly (it is the - * fallback). If this assumption ever changes, isHandlerRegistered() above - * would falsely mark the DefaultHandler-safe types as "handled", hiding - * real gaps. + * The handler registry never lists DefaultHandler explicitly; it is the + * fallback. Were it registered, isHandlerRegistered() would mark every + * DefaultHandler-safe type as handled and hide real gaps. */ public function testDefaultHandlerIsNotRegistered(): void { $property = new \ReflectionProperty(Core::class, 'fieldHandlers'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/FileHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/FileHandlerKernelTest.php index 897b9f89..b5835bc6 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/FileHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/FileHandlerKernelTest.php @@ -13,10 +13,9 @@ /** * Kernel round-trip test for FileHandler via the Core backend. * - * FileHandler reads a source file from disk, writes it into public:// via the - * file.repository service, saves a managed File entity, and emits a reference - * payload (target_id, display, description). This kernel test runs the whole - * chain against real storage and asserts the reference round-trips. + * FileHandler reads a source file from disk and writes it into public:// via + * the file.repository service. It then saves a managed File entity and emits + * a reference payload (target_id, display, description). */ #[CoversClass(FileHandler::class)] #[Group('fields')] diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/LinkHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/LinkHandlerKernelTest.php index 35a43f49..2fc7051c 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/LinkHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/LinkHandlerKernelTest.php @@ -13,8 +13,8 @@ * Kernel round-trip test for link fields via the Core backend. * * Link is a multi-property field (uri, title, options). This test verifies - * the base class helper handles associative-array deltas correctly and that - * LinkHandler's output - including the enforced empty 'options' array - + * that the base helper handles associative-array deltas and that + * LinkHandler's output, including the enforced empty 'options' array, * round-trips through real storage. */ #[CoversClass(LinkHandler::class)] @@ -32,9 +32,6 @@ class LinkHandlerKernelTest extends FieldHandlerKernelTestBase { 'link', ]; - /** - * Tests round-trip for a link with both uri and title. - */ public function testLinkWithTitleRoundTrip(): void { $this->attachField('field_homepage', 'link'); @@ -48,8 +45,8 @@ public function testLinkWithTitleRoundTrip(): void { * * LinkHandler converts a bare string into ['uri' => $string] during expand, * so the backend-mutated stub holds an array after entityCreate. The base - * assertion compares that mutated array against the stored field - which - * proves the scalar-to-array normalization reached storage intact. + * assertion compares that array against the stored field and so checks + * that the scalar-to-array normalization reached storage intact. */ public function testUriOnlyStringRoundTrip(): void { $this->attachField('field_homepage', 'link'); diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/ListFloatHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/ListFloatHandlerKernelTest.php index 08984d2f..c8315213 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/ListFloatHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/ListFloatHandlerKernelTest.php @@ -27,13 +27,10 @@ class ListFloatHandlerKernelTest extends FieldHandlerKernelTestBase { 'options', ]; - /** - * Tests that a label is translated to its float key on round-trip. - */ public function testLabelToFloatKeyRoundTrip(): void { - // Use fractional-only keys so the stored value actually exercises float - // handling; keys like '1.0' normalise to integer '1' on storage and would - // not distinguish list_float from list_integer. + // Use fractional-only keys so the stored value exercises float handling; + // a key like '1.0' normalises to the integer '1' in storage and would not + // distinguish list_float from list_integer. $this->attachField('field_rating', 'list_float', [ 'allowed_values' => [ '0.5' => 'Half', diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/ListIntegerHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/ListIntegerHandlerKernelTest.php index 83f0ab6d..301216a3 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/ListIntegerHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/ListIntegerHandlerKernelTest.php @@ -14,9 +14,9 @@ /** * Kernel round-trip test for ListIntegerHandler via the Core backend. * - * ListIntegerHandler inherits ListHandlerBase, so the label-to-key translation - * behaviour mirrors ListStringHandler; the difference is storage stores an - * integer, not a string. This test verifies the integer key round-trips. + * ListIntegerHandler inherits ListHandlerBase, so its label-to-key + * translation matches ListStringHandler's; the difference is that storage + * holds an integer, not a string. */ #[CoversClass(ListIntegerHandler::class)] #[Group('fields')] @@ -33,9 +33,6 @@ class ListIntegerHandlerKernelTest extends FieldHandlerKernelTestBase { 'options', ]; - /** - * Tests that a label is translated to its integer key on round-trip. - */ public function testLabelToIntegerKeyRoundTrip(): void { $this->attachField('field_priority', 'list_integer', [ 'allowed_values' => [ @@ -45,12 +42,10 @@ public function testLabelToIntegerKeyRoundTrip(): void { ], ]); - // Pass the label; handler replaces with integer key 2. $this->assertFieldRoundTripViaBackend('field_priority', ['Medium']); - // Pin the translation explicitly so a regression where the handler stops - // converting labels to keys is caught even though the mutated-stub - // round-trip would otherwise pass. + // The mutated-stub round-trip passes even when the handler stops + // converting labels to keys, so the key is asserted explicitly. $stub = new EntityStub('entity_test', 'entity_test', [ 'name' => 'pinned', 'field_priority' => ['Medium'], diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/ListStringHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/ListStringHandlerKernelTest.php index 588c7130..325e308f 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/ListStringHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/ListStringHandlerKernelTest.php @@ -16,9 +16,7 @@ * * The list_string field is single-property but its handler translates labels * to the machine keys declared in the field's allowed_values storage - * setting. This test exercises that translation end-to-end: the backend - * receives a label, the handler swaps it for the key, storage accepts the - * key, and the round-trip returns the key unchanged. + * setting. This test exercises that translation end-to-end. */ #[CoversClass(ListStringHandler::class)] #[Group('fields')] @@ -35,9 +33,6 @@ class ListStringHandlerKernelTest extends FieldHandlerKernelTestBase { 'options', ]; - /** - * Tests that a label is translated to its allowed_values key on round-trip. - */ public function testLabelToKeyRoundTrip(): void { $this->attachField('field_status', 'list_string', [ 'allowed_values' => [ @@ -46,14 +41,10 @@ public function testLabelToKeyRoundTrip(): void { ], ]); - // Pass the label; the handler replaces it with 'active' (the key). - // After the backend mutates the stub, the assertion compares the key - // against what storage returned. $this->assertFieldRoundTripViaBackend('field_status', ['Active']); - // Pin the translation explicitly so a regression where the handler stops - // converting labels to keys is caught even though the mutated-stub - // round-trip would otherwise pass. + // The mutated-stub round-trip passes even when the handler stops + // converting labels to keys, so the key is asserted explicitly. $stub = new EntityStub('entity_test', 'entity_test', [ 'name' => 'pinned', 'field_status' => ['Active'], @@ -63,9 +54,6 @@ public function testLabelToKeyRoundTrip(): void { $this->assertSame('active', $reloaded->get('field_status')->value); } - /** - * Tests that a value already equal to an allowed key round-trips as-is. - */ public function testKeyPassesThroughRoundTrip(): void { $this->attachField('field_status', 'list_string', [ 'allowed_values' => [ diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/NameHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/NameHandlerKernelTest.php index e3d3c7b5..90f72144 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/NameHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/NameHandlerKernelTest.php @@ -15,11 +15,12 @@ * Kernel round-trip test for NameHandler via the Core backend. * * Name is a multi-property field provided by the 'drupal/name' contrib - * module. The handler accepts three input shapes (shorthand string, - * numeric array, associative array) and normalises them into the same - * per-component keyed structure. It also honours the field's - * 'components' setting: positional input skips disabled components and - * named input throws if it targets one. + * module. The handler accepts 3 input shapes (shorthand string, numeric + * array, associative array) and normalises them into the same per-component + * keyed structure. + * + * The handler also honours the field's 'components' setting: positional input + * skips disabled components and named input throws if it targets one. */ #[CoversClass(NameHandler::class)] #[Group('fields')] @@ -36,9 +37,6 @@ class NameHandlerKernelTest extends FieldHandlerKernelTestBase { 'name', ]; - /** - * Tests round-trip for a name field with associative input. - */ public function testNameAssociativeRoundTrip(): void { $this->attachField('field_author', 'name'); @@ -59,7 +57,7 @@ public function testNameShorthandStringRoundTrip(): void { $this->assertFieldRoundTripViaBackend('field_author', ['Doe, Jane']); // Pin the component split explicitly: the mutated-stub round-trip would - // still pass if the handler swapped the two components. + // still pass if the handler swapped the 2 components. $stub = new EntityStub('entity_test', 'entity_test', [ 'name' => 'pinned', 'field_author' => ['Doe, Jane'], @@ -70,9 +68,6 @@ public function testNameShorthandStringRoundTrip(): void { $this->assertSame('Doe', $values[0]['family']); } - /** - * Tests positional input maps into enabled components only. - */ public function testNamePositionalSkipsDisabledComponents(): void { $this->attachField('field_author', 'name', [], [ 'components' => [ @@ -89,8 +84,8 @@ public function testNamePositionalSkipsDisabledComponents(): void { ['Dr', 'Jane', 'Doe'], ]); - // Pin the positional mapping: the values must land on the three enabled - // components in canonical order, leaving the disabled ones empty. + // Pin the positional mapping: the values must fill the 3 enabled + // components in canonical order and leave the disabled ones empty. $stub = new EntityStub('entity_test', 'entity_test', [ 'name' => 'pinned', 'field_author' => [['Dr', 'Jane', 'Doe']], diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/SmartdateHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/SmartdateHandlerKernelTest.php index 0f78f3e6..687c5f96 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/SmartdateHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/SmartdateHandlerKernelTest.php @@ -12,11 +12,12 @@ /** * Kernel round-trip test for SmartdateHandler via the Core backend. * - * SmartdateHandler emits a six-column payload ('value', 'end_value', - * 'duration', 'rrule', 'rrule_index', 'timezone'). This test proves the - * backend resolves SmartdateHandler for type 'smartdate' and that the - * multi-column storage accepts what the handler emits. The 'smartdate' field - * type is provided by drupal/smart_date. + * SmartdateHandler emits a 6-column payload ('value', 'end_value', + * 'duration', 'rrule', 'rrule_index', 'timezone'). The 'smartdate' field type + * is provided by drupal/smart_date. + * + * This test proves the backend resolves SmartdateHandler for type 'smartdate' + * and that the multi-column storage accepts what the handler emits. */ #[CoversClass(SmartdateHandler::class)] #[Group('fields')] diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/SupportedImageHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/SupportedImageHandlerKernelTest.php index f0b808ab..965882a9 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/SupportedImageHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/SupportedImageHandlerKernelTest.php @@ -15,7 +15,8 @@ * * The 'supported_image' field (provided by drupal/supported_image) adds * caption and attribution columns on top of the standard image file reference. - * The handler mirrors ImageHandler's disk read/write but emits richer payload. + * The handler mirrors ImageHandler's disk read/write but emits a payload with + * those extra columns. */ #[CoversClass(SupportedImageHandler::class)] #[Group('fields')] diff --git a/tests/phpunit/src/Kernel/Backend/Core/Field/TimeHandlerKernelTest.php b/tests/phpunit/src/Kernel/Backend/Core/Field/TimeHandlerKernelTest.php index 86a84797..e7e17f5d 100644 --- a/tests/phpunit/src/Kernel/Backend/Core/Field/TimeHandlerKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/Core/Field/TimeHandlerKernelTest.php @@ -12,8 +12,8 @@ /** * Kernel round-trip test for TimeHandler via the Core backend. * - * TimeHandler accepts a numeric number of seconds past midnight or a - * parseable time string (e.g. "9:30 AM") and emits the storage integer. + * TimeHandler accepts a numeric seconds-past-midnight value or a parseable + * time string (e.g. "9:30 AM") and emits the storage integer. * The 'time' field type is provided by drupal/time_field. */ #[CoversClass(TimeHandler::class)] @@ -31,9 +31,6 @@ class TimeHandlerKernelTest extends FieldHandlerKernelTestBase { 'time_field', ]; - /** - * Tests round-trip for a time field with a numeric seconds value. - */ public function testTimeNumericRoundTrip(): void { $this->attachField('field_start', 'time'); diff --git a/tests/phpunit/src/Kernel/Backend/DrupalBackendConstructionKernelTest.php b/tests/phpunit/src/Kernel/Backend/DrupalBackendConstructionKernelTest.php index 652cadde..301dd07f 100644 --- a/tests/phpunit/src/Kernel/Backend/DrupalBackendConstructionKernelTest.php +++ b/tests/phpunit/src/Kernel/Backend/DrupalBackendConstructionKernelTest.php @@ -54,9 +54,6 @@ public function testConstructorRejectsMissingRoot(): void { new DrupalBackend('/nonexistent/path/that/will/never/exist', 'default'); } - /** - * Tests 'setCoreFromVersion()' picks the default Core. - */ public function testSetCoreFromVersionSelectsDefaultCore(): void { $backend = new DrupalBackend($this->root, 'default'); $backend->setCoreFromVersion(); diff --git a/tests/phpunit/src/Kernel/Helper/Drupal/EntityLifecycleTraitVocabularyKernelTest.php b/tests/phpunit/src/Kernel/Helper/Drupal/EntityLifecycleTraitVocabularyKernelTest.php index 5bb79d5b..cfe291c6 100644 --- a/tests/phpunit/src/Kernel/Helper/Drupal/EntityLifecycleTraitVocabularyKernelTest.php +++ b/tests/phpunit/src/Kernel/Helper/Drupal/EntityLifecycleTraitVocabularyKernelTest.php @@ -59,16 +59,10 @@ protected function setUp(): void { $this->context->setDispatcher(new HookDispatcher(new HookRepository(new EnvironmentManager()), new CallCenter())); } - /** - * Tests that a machine name is returned untouched. - */ public function testMachineNameResolvesToItself(): void { $this->assertSame('tags', $this->context->callResolveVocabularyMachineName('tags')); } - /** - * Tests that a human label resolves to the vocabulary's machine name. - */ public function testLabelResolvesToItsMachineName(): void { $this->assertSame('tags', $this->context->callResolveVocabularyMachineName('Tags')); } @@ -80,9 +74,6 @@ public function testAnUnknownIdentifierIsReturnedUnchanged(): void { $this->assertSame('Unknown', $this->context->callResolveVocabularyMachineName('Unknown')); } - /** - * Tests that term creation resolves the label before calling the backend. - */ public function testTermCreationResolvesTheVocabularyLabel(): void { $stub = new EntityStub('taxonomy_term', 'tags', ['name' => 'A term', 'vocabulary_machine_name' => 'Tags']); @@ -100,9 +91,6 @@ public function testTermCreationResolvesTheVocabularyLabel(): void { $this->assertSame('tags', $stub->getValue('vocabulary_machine_name')); } - /** - * Tests that a backend without Drupal receives the identifier as given. - */ public function testTermCreationLeavesLabelForNonDrupalBackend(): void { $stub = new EntityStub('taxonomy_term', 'tags', ['name' => 'A term', 'vocabulary_machine_name' => 'Tags']); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/ContentBlockTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/ContentBlockTraitKernelTest.php index 4ba6f473..85cc9682 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/ContentBlockTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/ContentBlockTraitKernelTest.php @@ -41,9 +41,6 @@ protected function setUp(): void { BlockContentType::create(['id' => 'other', 'label' => 'Other'])->save(); } - /** - * Tests that the matching content blocks of the type are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingContentBlocks(): void { $first = $this->createContentBlock('basic', 'Shared'); $second = $this->createContentBlock('basic', 'Shared'); @@ -55,9 +52,6 @@ public function testLoadMultipleLoadsTheMatchingContentBlocks(): void { $this->assertLoadedSet([$first, $second], $content_blocks, BlockContentInterface::class); } - /** - * Tests that an empty array is returned when no content block matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createContentBlock('other', 'Shared'); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/EckTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/EckTraitKernelTest.php index 4d29253e..a1e4411c 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/EckTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/EckTraitKernelTest.php @@ -43,9 +43,6 @@ protected function setUp(): void { $bundle_storage->create(['type' => 'company', 'name' => 'Company'])->save(); } - /** - * Tests that the matching entities of the bundle are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingEntities(): void { $first = $this->createEntity('person', 'Shared'); $second = $this->createEntity('person', 'Shared'); @@ -57,9 +54,6 @@ public function testLoadMultipleLoadsTheMatchingEntities(): void { $this->assertLoadedSet([$first, $second], $entities, ContentEntityInterface::class); } - /** - * Tests that an empty array is returned when no entity matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createEntity('company', 'Shared'); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/FileTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/FileTraitKernelTest.php index ad257d04..aad0ae01 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/FileTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/FileTraitKernelTest.php @@ -36,9 +36,6 @@ protected function setUp(): void { $this->installEntitySchema('file'); } - /** - * Tests that the matching files are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingFiles(): void { $first = $this->createFile('public://first/shared.txt'); $second = $this->createFile('public://second/shared.txt'); @@ -49,9 +46,6 @@ public function testLoadMultipleLoadsTheMatchingFiles(): void { $this->assertLoadedSet([$first, $second], $files, FileInterface::class); } - /** - * Tests that an empty array is returned when no file matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createFile('public://other.txt'); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/MediaTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/MediaTraitKernelTest.php index cf883076..a924bf04 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/MediaTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/MediaTraitKernelTest.php @@ -48,9 +48,6 @@ protected function setUp(): void { $this->createMediaType('test', ['id' => 'image']); } - /** - * Tests that the matching media of the type are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingMedia(): void { $first = $this->createMedia('document', 'Shared'); $second = $this->createMedia('document', 'Shared'); @@ -62,9 +59,6 @@ public function testLoadMultipleLoadsTheMatchingMedia(): void { $this->assertLoadedSet([$first, $second], $media, MediaInterface::class); } - /** - * Tests that an empty array is returned when no media matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createMedia('image', 'Shared'); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/TaxonomyTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/TaxonomyTraitKernelTest.php index bdb070c6..e2958103 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/TaxonomyTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/TaxonomyTraitKernelTest.php @@ -41,9 +41,6 @@ protected function setUp(): void { Vocabulary::create(['vid' => 'topics', 'name' => 'Topics'])->save(); } - /** - * Tests that the matching terms of the vocabulary are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingTerms(): void { $first = $this->createTerm('tags', 'Shared'); $second = $this->createTerm('tags', 'Shared'); @@ -75,9 +72,6 @@ public function testLoadMultipleKeysByIdNotRevisionId(): void { $this->assertLoadedSet([$first, $second], $terms, TermInterface::class); } - /** - * Tests that an empty array is returned when no term matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createTerm('topics', 'Shared'); diff --git a/tests/phpunit/src/Kernel/Steps/Drupal/UserTraitKernelTest.php b/tests/phpunit/src/Kernel/Steps/Drupal/UserTraitKernelTest.php index 16eaf0fc..772efb66 100644 --- a/tests/phpunit/src/Kernel/Steps/Drupal/UserTraitKernelTest.php +++ b/tests/phpunit/src/Kernel/Steps/Drupal/UserTraitKernelTest.php @@ -36,9 +36,6 @@ protected function setUp(): void { $this->installEntitySchema('user'); } - /** - * Tests that the matching users are loaded, keyed by ID. - */ public function testLoadMultipleLoadsTheMatchingUsers(): void { $first = $this->createUser('first', 1); $second = $this->createUser('second', 1); @@ -49,18 +46,12 @@ public function testLoadMultipleLoadsTheMatchingUsers(): void { $this->assertLoadedSet([$first, $second], $users, UserInterface::class); } - /** - * Tests that an empty array is returned when no user matches. - */ public function testLoadMultipleReturnsAnEmptyArrayWhenNothingMatches(): void { $this->createUser('first', 1); $this->assertSame([], $this->context->userLoadMultiple(['name' => 'missing'])); } - /** - * Tests that a created role carries the permissions it was given. - */ public function testCreateRoleGrantsThePermissions(): void { $this->context->userCreateRole('Editor', 'access user profiles, change own username'); diff --git a/tests/phpunit/src/LintLayersTest.php b/tests/phpunit/src/LintLayersTest.php index de42af51..921b644e 100644 --- a/tests/phpunit/src/LintLayersTest.php +++ b/tests/phpunit/src/LintLayersTest.php @@ -24,9 +24,6 @@ protected function setUp(): void { require_once __DIR__ . '/../../../scripts/lint-layers.php'; } - /** - * Assert that every shipped layer holds its rule. - */ public function testShippedLayersAreClean(): void { $root = dirname(__DIR__, 3); $violations = []; @@ -42,9 +39,6 @@ public function testShippedLayersAreClean(): void { $this->assertSame([], $violations); } - /** - * Assert that only PHP files are collected, in path order. - */ public function testFilesCollectsPhpFilesRecursively(): void { $this->writeFixture('Nested/Second.php', 'writeFixture('First.php', 'assertSame($expected, layer_files(static::$tmp)); } - /** - * Assert that a path naming one file collects that file alone. - */ public function testFilesCollectsSingleFile(): void { $file = $this->writeFixture('Only.php', 'assertSame([], traits_violations($traits)); } - /** - * Assert that a trait's kind is read from its directory. - */ public function testCollectReadsBothDirectories(): void { $this->writeFixture('src/Steps/Web/PathTrait.php', "writeFixture('src/Steps/Web/README.md', 'not code'); @@ -51,9 +45,6 @@ public function testCollectReadsBothDirectories(): void { $this->assertSame('helper', $collected['StringTrait']['kind']); } - /** - * Assert that collection skips a missing directory and reads the rest. - */ public function testCollectSkipsMissingDirectory(): void { $this->writeFixture('src/Helper/StringTrait.php', "assertSame($expected, traits_file_facts($file)); } - /** - * Fixture rows for the fact reader. - */ public static function dataProviderFileFacts(): array { return [ 'bare trait' => [ @@ -136,9 +124,6 @@ public function testViolations(array $traits, array $expected): void { $this->assertSame($expected, traits_violations($traits)); } - /** - * Fixture rows for the violation check. - */ public static function dataProviderViolations(): array { $steps = ['kind' => 'steps', 'composed' => [], 'members' => []]; $helper = ['kind' => 'helper', 'composed' => [], 'members' => []]; diff --git a/tests/phpunit/src/MemberOrderTest.php b/tests/phpunit/src/MemberOrderTest.php index eec3ba6f..d51926a1 100644 --- a/tests/phpunit/src/MemberOrderTest.php +++ b/tests/phpunit/src/MemberOrderTest.php @@ -121,9 +121,9 @@ protected static function orderedMembers(string $trait): array { $members = []; - // A composition is read from the source rather than from - // ReflectionClass::getTraitNames(), which reports the composed trait's - // real name and so cannot be matched against an aliased import. + // ReflectionClass::getTraitNames() reports the composed trait's real + // name, so it cannot be matched against an aliased import. The + // composition is read from the source instead. foreach ($lines as $index => $line) { if (preg_match('/^\s+use\s+([A-Za-z\\\\][\w\\\\]*)\s*;/', $line, $matches) === 1) { $members[] = ['name' => 'use ' . $matches[1], 'group' => static::GROUP_COMPOSITION, 'line' => $index + 1]; diff --git a/tests/phpunit/src/ProvisionTest.php b/tests/phpunit/src/ProvisionTest.php index 625e4b5e..e1d47966 100644 --- a/tests/phpunit/src/ProvisionTest.php +++ b/tests/phpunit/src/ProvisionTest.php @@ -11,10 +11,10 @@ /** * Tests the fixture site provisioning script. * - * The provisioning sequence itself is exercised by the CI matrix, which - * builds a real site on every leg. What is covered here is the logic that - * shapes the build: the Composer merge, the paths it rebases, the patch map, - * and the 2 rewrites that a Drupal 12 build depends on. + * The CI matrix builds a real site on every leg, so it exercises the + * provisioning sequence itself. This test covers the logic that shapes the + * build: the Composer merge, the paths it rebases, the patch map, and the 2 + * rewrites that a Drupal 12 build depends on. */ #[CoversFunction('provision_append_settings')] #[CoversFunction('provision_behat_packages')] @@ -61,9 +61,6 @@ public function testEnvFallsBack(string $assignment): void { putenv('PROVISION_TEST_VARIABLE'); } - /** - * Fixture rows for the environment fallback. - */ public static function dataProviderEnvFallsBack(): array { return [ 'unset' => ['PROVISION_TEST_VARIABLE'], @@ -71,9 +68,6 @@ public static function dataProviderEnvFallsBack(): array { ]; } - /** - * Assert that a set variable wins over the default. - */ public function testEnvReadsTheValue(): void { putenv('PROVISION_TEST_VARIABLE=10'); @@ -82,16 +76,10 @@ public function testEnvReadsTheValue(): void { putenv('PROVISION_TEST_VARIABLE'); } - /** - * Assert that a command without variables is left alone. - */ public function testWithEnvLeavesTheCommandAlone(): void { $this->assertSame('composer install', provision_with_env('composer install', [])); } - /** - * Assert that variables are prefixed and quoted. - */ public function testWithEnvPrefixesTheAssignments(): void { $expected = "/usr/bin/env COMPOSER_MEMORY_LIMIT='-1' PHP_OPTIONS='-d sendmail_path=/bin/true' composer install"; $env = ['COMPOSER_MEMORY_LIMIT' => '-1', 'PHP_OPTIONS' => '-d sendmail_path=/bin/true']; @@ -99,9 +87,6 @@ public function testWithEnvPrefixesTheAssignments(): void { $this->assertSame($expected, provision_with_env('composer install', $env)); } - /** - * Assert that a section is read, and that a missing one reads as empty. - */ public function testSectionReadsAnObjectOnly(): void { $config = ['require' => ['drupal/core' => '^11'], 'name' => 'drevops/fixture']; @@ -110,18 +95,12 @@ public function testSectionReadsAnObjectOnly(): void { $this->assertSame([], provision_section($config, 'name')); } - /** - * Assert that a JSON file is read and decoded. - */ public function testReadJsonDecodesTheFile(): void { $file = $this->writeFixture('composer.json', '{"name": "drevops/fixture"}'); $this->assertSame(['name' => 'drevops/fixture'], provision_read_json($file)); } - /** - * Assert that a missing file is reported. - */ public function testReadJsonReportsTheMissingFile(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Unable to read'); @@ -129,9 +108,6 @@ public function testReadJsonReportsTheMissingFile(): void { provision_read_json(static::$tmp . '/absent.json'); } - /** - * Assert that contents that are not an object are reported. - */ public function testReadJsonReportsContentsThatAreNotAnObject(): void { $file = $this->writeFixture('broken.json', 'not json'); @@ -141,16 +117,10 @@ public function testReadJsonReportsContentsThatAreNotAnObject(): void { provision_read_json($file); } - /** - * Assert that contents already in hand are decoded without a second read. - */ public function testDecodeJsonDecodesTheContents(): void { $this->assertSame(['name' => 'drevops/fixture'], provision_decode_json('{"name": "drevops/fixture"}', 'composer.json')); } - /** - * Assert that contents that do not decode name the file they came from. - */ public function testDecodeJsonNamesTheFileItCannotDecode(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Unable to decode /app/composer.json'); @@ -158,9 +128,6 @@ public function testDecodeJsonNamesTheFileItCannotDecode(): void { provision_decode_json('not json', '/app/composer.json'); } - /** - * Assert that a write that did not land is reported. - */ public function testWriteReportsTheFailedWrite(): void { UnwritableStream::register(); @@ -172,9 +139,6 @@ public function testWriteReportsTheFailedWrite(): void { }); } - /** - * Assert that a build without a token writes no auth file. - */ public function testWriteAuthSkipsWithoutToken(): void { $file = static::$tmp . '/auth.json'; @@ -183,9 +147,6 @@ public function testWriteAuthSkipsWithoutToken(): void { $this->assertFileDoesNotExist($file); } - /** - * Assert that the token is written, readable by its owner alone. - */ public function testWriteAuthWritesTheTokenPrivately(): void { $file = static::$tmp . '/auth.json'; @@ -195,9 +156,6 @@ public function testWriteAuthWritesTheTokenPrivately(): void { $this->assertSame(0600, fileperms($file) & 0777); } - /** - * Assert that the umask is restored when the write fails. - */ public function testWriteAuthRestoresTheUmask(): void { UnwritableStream::register(); $before = umask(); @@ -207,16 +165,13 @@ public function testWriteAuthRestoresTheUmask(): void { provision_write_auth('gh-token', UnwritableStream::path('auth.json')); } catch (\RuntimeException) { - // The restored umask is what is under test, not the report. + // The restored umask is under test, not the report. } }); $this->assertSame($before, umask()); } - /** - * Assert that each patch is keyed by the package its directory names. - */ public function testPatchesAreKeyedByTheirDirectory(): void { $this->writeFixture('patches/mglaman/phpstan-drupal/custom-drupal-root.patch', 'diff'); $this->writeFixture('patches/drupal/webform/fix-the-thing.patch', 'diff'); @@ -229,16 +184,10 @@ public function testPatchesAreKeyedByTheirDirectory(): void { $this->assertSame($expected, provision_patches(static::$tmp . '/patches', static::$tmp)); } - /** - * Assert that a tree holding no patch declares none. - */ public function testPatchesReadsAnEmptyTreeAsNone(): void { $this->assertSame([], provision_patches(static::$tmp . '/absent', static::$tmp)); } - /** - * Assert that a file that is not a patch is passed over. - */ public function testPatchesSkipsFilesThatAreNotPatches(): void { $this->writeFixture('patches/mglaman/phpstan-drupal/README.md', 'not a patch'); @@ -260,9 +209,6 @@ public function testBehatPackagesListsTheRemovals(string $behat, string $drupal_ $this->assertSame($expected, provision_behat_packages($behat, $drupal_version)); } - /** - * Fixture rows for the Behat package removals. - */ public static function dataProviderBehatPackagesListsTheRemovals(): array { return [ 'behat 3 removes nothing' => ['3', '11', []], @@ -284,9 +230,6 @@ public function testIsLenientFromTwelve(string $drupal_version, bool $expected): $this->assertSame($expected, provision_is_lenient($drupal_version)); } - /** - * Fixture rows for the lenient majors. - */ public static function dataProviderIsLenientFromTwelve(): array { return [ 'ten' => ['10', FALSE], @@ -296,9 +239,6 @@ public static function dataProviderIsLenientFromTwelve(): array { ]; } - /** - * Assert that the plugin constraint is read from the fixture. - */ public function testLenientConstraintReadsTheFixturePin(): void { $file = $this->writeFixture('d12/composer.json', '{"require": {"mglaman/composer-drupal-lenient": "^2.0"}}'); @@ -321,9 +261,6 @@ public function testLenientConstraintReportsTheMissingPin(string $contents): voi provision_lenient_constraint($file); } - /** - * Fixture rows for the missing lenient pin. - */ public static function dataProviderLenientConstraintReportsTheMissingPin(): array { return [ 'no require section' => ['{"name": "drevops/fixture"}'], @@ -348,9 +285,6 @@ public function testInstallCommandNarrowsBehat(string $deps, string $behat, stri $this->assertSame($expected, provision_install_command($deps, $behat)); } - /** - * Fixture rows for the install command. - */ public static function dataProviderInstallCommandNarrowsBehat(): array { return [ 'normal' => ['normal', '3', "composer update --with='behat/behat:^3'"], @@ -371,9 +305,6 @@ public function testWidenCoreRequirementAdmitsTheMajor(string $text, string $exp $this->assertSame($expected, provision_widen_core_requirement($text, '12')); } - /** - * Fixture rows for the core version requirement rewrite. - */ public static function dataProviderWidenCoreRequirementAdmitsTheMajor(): array { return [ 'bare constraint' => [ @@ -411,9 +342,6 @@ public static function dataProviderWidenCoreRequirementAdmitsTheMajor(): array { ]; } - /** - * Assert that every extension of the tree is reached and counted. - */ public function testWidenContribRewritesTheWholeTree(): void { $token = $this->writeFixture('contrib/token/token.info.yml', "core_version_requirement: ^11\n"); $nested = $this->writeFixture('contrib/token/modules/token_extra/token_extra.info.yml', "core_version_requirement: ^11\n"); @@ -428,16 +356,10 @@ public function testWidenContribRewritesTheWholeTree(): void { $this->assertSame('core_version_requirement: ^11', file_get_contents($readme)); } - /** - * Assert that a build that installed no contrib widens nothing. - */ public function testWidenContribReadsAnAbsentTreeAsNone(): void { $this->assertSame(0, provision_widen_contrib(static::$tmp . '/absent', '12')); } - /** - * Assert that the merge shapes the fixture's Composer configuration. - */ public function testMergeComposerShapesTheFixture(): void { $merged = provision_merge_composer(static::package(), static::fixture()); @@ -456,27 +378,18 @@ public function testMergeComposerShapesTheFixture(): void { $this->assertSame($expected_require_dev, $merged['require-dev']); } - /** - * Assert that the PHP constraint does not reach the fixture. - */ public function testMergeComposerDropsThePhpConstraint(): void { $merged = provision_merge_composer(static::package(), static::fixture()); $this->assertArrayNotHasKey('php', $merged['require-dev']); } - /** - * Assert that a "require-dev" entry without a "suggest" entry is dropped. - */ public function testMergeComposerDropsAnUnsuggestedDevPackage(): void { $merged = provision_merge_composer(static::package(), static::fixture()); $this->assertArrayNotHasKey('phpstan/phpstan', $merged['require-dev']); } - /** - * Assert that the fixture's own "require" survives the merge. - */ public function testMergeComposerKeepsTheFixtureRequire(): void { $merged = provision_merge_composer(static::package(), static::fixture()); @@ -497,9 +410,6 @@ public function testMergeComposerDropsDevPackageTheFixturePins(): void { $this->assertArrayNotHasKey('drupal/core-recommended', $merged['require-dev']); } - /** - * Assert that every autoload path is rebased on the package root. - */ public function testMergeComposerRebasesTheAutoloadPaths(): void { $merged = provision_merge_composer(static::package(), static::fixture()); @@ -525,9 +435,6 @@ public function testMergeComposerRebasesThePackageTestNamespaces(): void { $this->assertSame($expected, array_slice($merged['autoload-dev']['psr-4'], 0, 3, TRUE)); } - /** - * Assert that Drupal's own test namespaces resolve from the docroot. - */ public function testMergeComposerRegistersTheDrupalTestNamespaces(): void { $merged = provision_merge_composer(static::package(), static::fixture()); @@ -544,9 +451,6 @@ public function testMergeComposerRegistersTheDrupalTestNamespaces(): void { $this->assertSame($expected, array_slice($merged['autoload-dev']['psr-4'], 3, NULL, TRUE)); } - /** - * Assert that the fixture's own properties survive the merge. - */ public function testMergeComposerKeepsTheFixtureProperties(): void { $merged = provision_merge_composer(static::package(), static::fixture()); @@ -554,18 +458,12 @@ public function testMergeComposerKeepsTheFixtureProperties(): void { $this->assertSame(['allow-plugins' => ['composer/installers' => TRUE]], $merged['config']); } - /** - * Assert that a section without PSR-4 entries is returned unchanged. - */ public function testRebasePsr4LeavesTheClassmapAlone(): void { $autoload = ['classmap' => ['scripts/composer/']]; $this->assertSame($autoload, provision_rebase_psr4($autoload)); } - /** - * Assert that a namespace mapped to a list keeps its shape. - */ public function testRebasePsr4RebasesEveryDirectoryOfList(): void { $autoload = ['psr-4' => ['DrevOps\\BehatSteps\\' => ['src/', 'lib/']]]; $expected = ['psr-4' => ['DrevOps\\BehatSteps\\' => ['../src/', '../lib/']]]; @@ -573,9 +471,6 @@ public function testRebasePsr4RebasesEveryDirectoryOfList(): void { $this->assertSame($expected, provision_rebase_psr4($autoload)); } - /** - * Assert that a PSR-4 path that is not a string does not reach the build. - */ public function testRebasePsr4ReadsNonPathValueAsEmpty(): void { $autoload = ['psr-4' => ['DrevOps\\BehatSteps\\' => 11]]; @@ -602,9 +497,6 @@ public function testMergeComposerLeavesTheFixturePsr4Alone(): void { $this->assertSame($expected, $merged['autoload']['psr-4']); } - /** - * Assert that the merged configuration is written as pretty JSON. - */ public function testWriteMergedComposerWritesTheResult(): void { $package_file = $this->writeFixture('package/composer.json', (string) json_encode(static::package())); $fixture_file = $this->writeFixture('build/composer.json', (string) json_encode(static::fixture())); @@ -618,9 +510,6 @@ public function testWriteMergedComposerWritesTheResult(): void { $this->assertStringNotContainsString('\/', $written); } - /** - * Assert that the config overrides are appended and the file closed again. - */ public function testAppendSettingsAppendsTheOverrides(): void { $file = $this->writeFixture('settings.php', "assertSame(0444, fileperms($file) & 0777); } - /** - * Assert that a settings file that is not there is reported. - */ public function testAppendSettingsReportsTheMissingFile(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Unable to open'); diff --git a/tests/phpunit/src/PublicSurfaceTest.php b/tests/phpunit/src/PublicSurfaceTest.php index ffbfd43d..46950a56 100644 --- a/tests/phpunit/src/PublicSurfaceTest.php +++ b/tests/phpunit/src/PublicSurfaceTest.php @@ -36,8 +36,10 @@ * * Visibility marks the API. A public method that Behat does not register is * the toolbox, published in HELPERS.md and covered by semantic versioning; a - * protected one carries no guarantee. A method cannot be narrowed again - * before the next major, so these tests hold the four conventions below. + * protected one carries no guarantee. + * + * A method cannot be narrowed again before the next major, so these tests + * hold the 4 conventions below. */ #[CoversNothing] class PublicSurfaceTest extends UnitTestCase { @@ -227,7 +229,7 @@ protected static function traitOwnMethods(string $trait): array { * Return the names of properties a trait receives from composed traits. * * Reflection flattens a composed trait's members into the composing trait - * and reports the composing trait as their declaring class, so the origin is + * and reports the composing trait as their declaring class. The origin is * resolved by name against the composed traits instead. * * @param \ReflectionClass $reflection diff --git a/tests/phpunit/src/StepScenarioCoverageTest.php b/tests/phpunit/src/StepScenarioCoverageTest.php index 268ce85a..6bc0d1ac 100644 --- a/tests/phpunit/src/StepScenarioCoverageTest.php +++ b/tests/phpunit/src/StepScenarioCoverageTest.php @@ -58,9 +58,6 @@ class StepScenarioCoverageTest extends UnitTestCase { */ protected const NESTED_FEATURE_PATTERN = '/^(?:there is )?a file named "[^"]*\.feature" with:$/'; - /** - * Assert that every registered step matches a step some scenario runs. - */ public function testEveryRegisteredStepIsRun(): void { $root = dirname(__DIR__, 3); $texts = static::scenarioStepTexts(glob($root . '/tests/behat/features/*.feature') ?: [], static::suiteFilter($root . '/behat.php')); @@ -286,9 +283,6 @@ public static function dataProviderScenarioStepTexts(): array { ]; } - /** - * Assert that a scenario is collected whatever its tags without a filter. - */ public function testScenarioStepTextsWithoutFilter(): void { $file = $this->writeFixture('features/subject.feature', "Feature: Subject\n @skipped\n Scenario: Skipped\n Given the skipped step\n"); @@ -393,9 +387,6 @@ public static function dataProviderUnexercisedSteps(): array { ]; } - /** - * Assert that the steps declared under the directory are discovered. - */ public function testRegisteredSteps(): void { $expected = [ ['label' => 'StepCoverageContext::contextStep()', 'pattern' => 'the context step should run'], @@ -408,9 +399,6 @@ public function testRegisteredSteps(): void { $this->assertSame($expected, static::registeredSteps(StepCoverageContext::class, __DIR__ . '/Fixtures/StepCoverage')); } - /** - * Assert that step discovery rejects a directory that does not exist. - */ public function testRegisteredStepsWithMissingDirectory(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('does not exist.'); @@ -500,8 +488,6 @@ protected static function collectStepTexts(FeatureNode $feature, ?TagFilter $fil $runs = []; - // The lowest supported Behat installs gherkin 4.17, which has no - // getExecutableChildren(). // @phpstan-ignore method.deprecated foreach ($feature->getScenarios() as $scenario) { $runs = array_merge($runs, $scenario instanceof OutlineNode ? $scenario->getExamples() : [$scenario]); diff --git a/tests/phpunit/src/TagReadTest.php b/tests/phpunit/src/TagReadTest.php index f6ed45b2..51e05bcf 100644 --- a/tests/phpunit/src/TagReadTest.php +++ b/tests/phpunit/src/TagReadTest.php @@ -12,7 +12,7 @@ * * A reader given the scope reads the scenario together with its feature, so a * tag on the 'Feature:' line applies to every scenario below it. A tag named - * in a constant is spelled once, and the constant is what every read shares. + * in a constant is spelled once, and every read shares the constant. */ #[CoversNothing] class TagReadTest extends UnitTestCase { diff --git a/tests/phpunit/src/TraitMethodNamingTest.php b/tests/phpunit/src/TraitMethodNamingTest.php index 8eeba7ce..691a2fd2 100644 --- a/tests/phpunit/src/TraitMethodNamingTest.php +++ b/tests/phpunit/src/TraitMethodNamingTest.php @@ -117,6 +117,7 @@ public static function dataProviderMethodsArePrefixed(): array { * * A `Then` step is an assertion, and so is a helper whose docblock opens * with "Assert". A hook is named for its event even when it asserts. + * * `Assert` appears nowhere else in a name, so the shape is always * `Assert`. * @@ -691,9 +692,8 @@ protected static function traitPrefix(string $short_name): string { /** * Check that a method name opens with the prefix as a whole word. * - * The character after the prefix must be uppercase so that a name which - * merely starts with the same letters, such as 'waiting' against 'wait', - * is not accepted. + * The character after the prefix must be uppercase, so a name that only + * starts with the same letters ('waiting' against 'wait') is not accepted. */ protected static function hasPrefix(string $method, string $prefix): bool { if ($method === $prefix) { diff --git a/tests/phpunit/src/Unit/Backend/Alias/RolesAliasTest.php b/tests/phpunit/src/Unit/Backend/Alias/RolesAliasTest.php index 9a5983ca..aea8524e 100644 --- a/tests/phpunit/src/Unit/Backend/Alias/RolesAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Alias/RolesAliasTest.php @@ -20,9 +20,6 @@ #[Group('aliases')] class RolesAliasTest extends TestCase { - /** - * Tests metadata accessors. - */ public function testMetadataAccessors(): void { $alias = new RolesAlias(new RecordingUserCapability()); @@ -79,9 +76,6 @@ public static function dataProviderApplyAfterCreateIgnoresNonArrayValues(): \Ite yield 'boolean false' => [FALSE]; } - /** - * Tests that an empty array is iterated zero times. - */ public function testApplyAfterCreateNoOpsOnEmptyArray(): void { $backend = new RecordingUserCapability(); $alias = new RolesAlias($backend); diff --git a/tests/phpunit/src/Unit/Backend/BlackboxBackendCreationAliasesTest.php b/tests/phpunit/src/Unit/Backend/BlackboxBackendCreationAliasesTest.php index 94cf7e51..1a1c4822 100644 --- a/tests/phpunit/src/Unit/Backend/BlackboxBackendCreationAliasesTest.php +++ b/tests/phpunit/src/Unit/Backend/BlackboxBackendCreationAliasesTest.php @@ -12,10 +12,6 @@ /** * Tests that 'BlackboxBackend' lacks the creation-alias capability. - * - * The backend declares no capabilities, so it does not implement - * 'CreationAliasCapabilityInterface'. Consumers must 'instanceof'-check - * before calling 'getCreationAliases()'. */ #[CoversClass(BlackboxBackend::class)] #[Group('backends')] @@ -23,9 +19,6 @@ #[Group('aliases')] class BlackboxBackendCreationAliasesTest extends TestCase { - /** - * Tests that BlackboxBackend does NOT implement the capability. - */ public function testDoesNotImplementCreationAliasCapability(): void { $this->assertNotContains(CreationAliasCapabilityInterface::class, (array) class_implements(BlackboxBackend::class)); } diff --git a/tests/phpunit/src/Unit/Backend/BlackboxBackendTest.php b/tests/phpunit/src/Unit/Backend/BlackboxBackendTest.php index c63f8dac..d884fe33 100644 --- a/tests/phpunit/src/Unit/Backend/BlackboxBackendTest.php +++ b/tests/phpunit/src/Unit/Backend/BlackboxBackendTest.php @@ -31,43 +31,28 @@ #[Group('blackbox')] class BlackboxBackendTest extends TestCase { - /** - * Tests that BlackboxBackend satisfies its declared interfaces. - */ public function testImplementsExpectedInterfaces(): void { $backend = new BlackboxBackend(); $this->assertInstanceOf(BlackboxBackendInterface::class, $backend); $this->assertInstanceOf(BackendInterface::class, $backend); } - /** - * Tests that 'isBootstrapped()' returns TRUE. - */ public function testIsBootstrappedReturnsTrue(): void { $backend = new BlackboxBackend(); $this->assertTrue($backend->isBootstrapped()); } - /** - * Tests that bootstrap() is a no-op. - */ public function testBootstrapIsNoop(): void { $backend = new BlackboxBackend(); $backend->bootstrap(); $this->addToAssertionCount(1); } - /** - * Tests that getRandom() returns a usable generator. - */ public function testGetRandomReturnsInstance(): void { $backend = new BlackboxBackend(); $this->assertInstanceOf(Random::class, $backend->getRandom()); } - /** - * Tests that an injected random generator is returned as-is. - */ public function testGetRandomReturnsInjectedInstance(): void { $random = new Random(); $backend = new BlackboxBackend($random); @@ -75,11 +60,11 @@ public function testGetRandomReturnsInjectedInstance(): void { } /** - * Tests that BlackboxBackend does not claim unsupported capabilities. - * - * @param string $capability_class - * The fully qualified capability interface name. - */ + * Tests that BlackboxBackend does not claim unsupported capabilities. + * + * @param string $capability_class + * The fully qualified capability interface name. + */ #[DataProvider('dataProviderDoesNotImplementCapability')] public function testDoesNotImplementCapability(string $capability_class): void { $this->assertNotContains($capability_class, (array) class_implements(BlackboxBackend::class), sprintf( diff --git a/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php b/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php index 5b067cc5..fcd52bce 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Alias/AuthorAliasTest.php @@ -21,9 +21,6 @@ #[Group('aliases')] class AuthorAliasTest extends TestCase { - /** - * Tests metadata accessors. - */ public function testMetadataAccessors(): void { $alias = new AuthorAlias(static fn(): ?object => NULL); diff --git a/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php b/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php index bb5a8e92..eb940dc5 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Alias/ParentTermAliasTest.php @@ -20,9 +20,6 @@ #[Group('aliases')] class ParentTermAliasTest extends TestCase { - /** - * Tests metadata accessors. - */ public function testMetadataAccessors(): void { $alias = new ParentTermAlias(static fn(): ?int => NULL); diff --git a/tests/phpunit/src/Unit/Backend/Core/Alias/VocabularyMachineNameAliasTest.php b/tests/phpunit/src/Unit/Backend/Core/Alias/VocabularyMachineNameAliasTest.php index e3562c03..7ba9e3f0 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Alias/VocabularyMachineNameAliasTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Alias/VocabularyMachineNameAliasTest.php @@ -19,9 +19,6 @@ #[Group('aliases')] class VocabularyMachineNameAliasTest extends TestCase { - /** - * Tests metadata accessors. - */ public function testMetadataAccessors(): void { $alias = new VocabularyMachineNameAlias(); diff --git a/tests/phpunit/src/Unit/Backend/Core/CoreErrorPathsTest.php b/tests/phpunit/src/Unit/Backend/Core/CoreErrorPathsTest.php index 3ab56a39..58fe5982 100644 --- a/tests/phpunit/src/Unit/Backend/Core/CoreErrorPathsTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/CoreErrorPathsTest.php @@ -30,9 +30,6 @@ protected function tearDown(): void { parent::tearDown(); } - /** - * Tests that the constructor throws when the root path cannot be resolved. - */ public function testConstructorThrowsWhenRootUnresolvable(): void { $this->expectException(BootstrapException::class); $this->expectExceptionMessageMatches('/Could not resolve Drupal root/'); @@ -55,9 +52,6 @@ public function testEntityCreateRejectsEmptyEntityType(): void { $core->entityCreate(new EntityStub('')); } - /** - * Tests that 'resolveUid()' throws when the stub carries no user id. - */ public function testResolveUidThrowsWhenStubHasNoId(): void { $core = $this->createCore(); $reflection = new \ReflectionMethod($core, 'resolveUid'); @@ -89,9 +83,6 @@ public function testLanguageMethodsRejectMissingLangcode(string $method, array $ $core->{$method}(new EntityStub('language', NULL, $values)); } - /** - * Data provider for 'testLanguageMethodsRejectMissingLangcode()'. - */ public static function dataProviderLanguageMethodsRejectMissingLangcode(): \Iterator { yield 'create without langcode' => ['languageCreate', []]; yield 'create with empty langcode' => ['languageCreate', ['langcode' => '']]; @@ -132,9 +123,6 @@ public function testEntityDeleteRejectsEmptyId(mixed $id): void { $core->entityDelete(new EntityStub('widget', NULL, ['id' => $id])); } - /** - * Data provider for 'testEntityDeleteRejectsEmptyId()'. - */ public static function dataProviderEntityDeleteRejectsEmptyId(): \Iterator { yield 'empty string' => ['']; yield 'null' => [NULL]; @@ -142,7 +130,7 @@ public static function dataProviderEntityDeleteRejectsEmptyId(): \Iterator { } /** - * Helper to build a Core instance pointed at a valid path. + * Builds a 'Core' instance pointed at a valid path. * * None of the error paths under test reach the filesystem, so any existing * directory serves as the root. diff --git a/tests/phpunit/src/Unit/Backend/Core/CoreFieldHandlerLookupTest.php b/tests/phpunit/src/Unit/Backend/Core/CoreFieldHandlerLookupTest.php index 0ef54276..dd76a358 100644 --- a/tests/phpunit/src/Unit/Backend/Core/CoreFieldHandlerLookupTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/CoreFieldHandlerLookupTest.php @@ -21,12 +21,6 @@ /** * Tests field handler resolution against the registry. - * - * Core's constructor calls 'registerDefaultFieldHandlers()' to populate - * built-in handlers, and consumers override via 'registerFieldHandler()'. - * These tests cover the three tiers: default (constructor-registered), - * consumer override, and fallback to 'DefaultHandler' for unknown field - * types. */ #[CoversClass(Core::class)] #[Group('core')] @@ -49,9 +43,6 @@ protected function tearDown(): void { parent::tearDown(); } - /** - * Tests that the constructor pre-registers the project's built-in handlers. - */ public function testConstructorRegistersBuiltInHandlers(): void { $core = new FieldTypeMapCore(__DIR__, 'default', ['field_address' => 'address']); @@ -60,9 +51,6 @@ public function testConstructorRegistersBuiltInHandlers(): void { $this->assertInstanceOf(AddressHandler::class, $handler); } - /** - * Tests that a consumer registration wins over the built-in handler. - */ public function testConsumerRegistrationOverridesBuiltIn(): void { $core = new FieldTypeMapCore(__DIR__, 'default', ['field_address' => 'address']); $core->registerFieldHandler('address', CustomFieldHandler::class); @@ -72,9 +60,6 @@ public function testConsumerRegistrationOverridesBuiltIn(): void { $this->assertInstanceOf(CustomFieldHandler::class, $handler); } - /** - * Tests that unknown field types fall back to 'DefaultHandler'. - */ public function testUnknownFieldTypeFallsBackToDefaultHandler(): void { $core = new FieldTypeMapCore(__DIR__, 'default', ['field_x' => 'nonexistent_type']); @@ -131,10 +116,10 @@ public function testRegisterRejectsAbstractHandlerClass(): void { /** * Sets up a minimal Drupal container satisfying AbstractHandler construction. * - * AbstractHandler's constructor pulls the entity field manager and the - * entity type manager off '\Drupal'; tests instantiate handlers via the - * registry, so both services must resolve. Storage and field definitions - * are stubbed loosely because no test depends on their shape. + * AbstractHandler's constructor reads the entity field manager and the + * entity type manager from '\Drupal'; tests instantiate handlers through + * the registry, so both services must resolve. Storage and field + * definitions are stubbed loosely because no test depends on their shape. */ protected function setUpDrupalContainer(): void { $field_definition = $this->createMock(FieldDefinitionInterface::class); @@ -142,8 +127,8 @@ protected function setUpDrupalContainer(): void { $storage_definition = $this->createMock(FieldStorageDefinitionInterface::class); $storage_definition->method('getType')->willReturn('string'); - // Consulted by Core's classifier gate when a field type falls back to - // DefaultHandler; a plain scalar (no properties) keeps the field + // Core's classifier gate reads the property definitions when a field type + // falls back to 'DefaultHandler'; a scalar with no properties stays // default-expandable. $storage_definition->method('getPropertyDefinitions')->willReturn([]); @@ -207,7 +192,7 @@ public function getEntityFieldTypes(string $entity_type, ?string $bundle = NULL) } /** - * Test handler used to verify consumer registrations win over defaults. + * Test handler used to verify consumer registrations override defaults. */ class CustomFieldHandler extends AbstractHandler { diff --git a/tests/phpunit/src/Unit/Backend/Core/CoreFieldMethodsTest.php b/tests/phpunit/src/Unit/Backend/Core/CoreFieldMethodsTest.php index 0ef2a507..ebea0401 100644 --- a/tests/phpunit/src/Unit/Backend/Core/CoreFieldMethodsTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/CoreFieldMethodsTest.php @@ -85,9 +85,6 @@ class TestCore extends Core { */ protected EntityFieldManagerInterface $entityFieldManager; - /** - * Sets the mock entity field manager. - */ public function setEntityFieldManager(EntityFieldManagerInterface $entity_field_manager): void { $this->entityFieldManager = $entity_field_manager; } diff --git a/tests/phpunit/src/Unit/Backend/Core/CorePermissionsTest.php b/tests/phpunit/src/Unit/Backend/Core/CorePermissionsTest.php index bbe796bd..fbcddcc7 100644 --- a/tests/phpunit/src/Unit/Backend/Core/CorePermissionsTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/CorePermissionsTest.php @@ -19,11 +19,9 @@ class CorePermissionsTest extends TestCase { /** * Tests that human-readable titles are converted to machine names. * - * Drupal returns permission titles as TranslatableMarkup objects. Strict - * comparison against plain string labels would never match, so the backend - * must cast the title to string before lookup. This guards against - * regressions where automated refactoring flips the comparison to strict - * mode without adding the cast. + * Drupal returns permission titles as TranslatableMarkup objects. A strict + * comparison against a plain string label never matches, so the backend + * casts the title to string before the lookup. */ public function testConvertPermissionsMapsStringableTitlesToMachineNames(): void { $core = new TestPermissionsCore(__DIR__, 'default'); @@ -42,9 +40,6 @@ public function testConvertPermissionsMapsStringableTitlesToMachineNames(): void $this->assertSame(['administer content types', 'administer users'], $permissions); } - /** - * Tests that titles already in machine name form are left unchanged. - */ public function testConvertPermissionsLeavesMachineNamesAlone(): void { $core = new TestPermissionsCore(__DIR__, 'default'); $core->setPermissions([ @@ -59,9 +54,6 @@ public function testConvertPermissionsLeavesMachineNamesAlone(): void { $this->assertSame(['administer users'], $permissions); } - /** - * Tests that 'checkPermissions()' passes for valid machine names. - */ public function testCheckPermissionsAcceptsValidMachineNames(): void { $core = new TestPermissionsCore(__DIR__, 'default'); $core->setPermissions([ @@ -75,9 +67,6 @@ public function testCheckPermissionsAcceptsValidMachineNames(): void { $this->assertSame(['administer users', 'access content'], $permissions); } - /** - * Tests that 'checkPermissions()' throws for unknown machine names. - */ public function testCheckPermissionsThrowsForUnknownPermission(): void { $core = new TestPermissionsCore(__DIR__, 'default'); $core->setPermissions([ @@ -126,9 +115,6 @@ protected function stringable(string $label): object { public function __construct(protected string $label) {} - /** - * Renders the stringable into its label. - */ public function __toString(): string { return $this->label; } diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerErrorPathsTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerErrorPathsTest.php index 22da5436..812faff8 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerErrorPathsTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerErrorPathsTest.php @@ -34,9 +34,6 @@ public function testConstructorRejectsEmptyEntityType(): void { new DefaultHandler(new EntityStub(''), '', 'field_any'); } - /** - * Tests that 'normalize()' rejects a handler without a main property. - */ public function testNormalizeRejectsMissingMainProperty(): void { $handler = (new \ReflectionClass(DefaultHandler::class))->newInstanceWithoutConstructor(); diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerNormalizeTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerNormalizeTest.php index 24b054b2..9586262b 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerNormalizeTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/AbstractHandlerNormalizeTest.php @@ -227,9 +227,6 @@ public static function dataProviderIsListOfRecords(): \Iterator { yield 'positional pair is a single record' => [['start', 'end'], FALSE]; yield 'keyed array is a single record' => [['value' => 'start'], FALSE]; yield 'mixed list leading with a scalar is a single record' => [['start', ['value' => 1]], FALSE]; - // A list with no element 0 exists only as the empty array, which every - // caller rejects earlier; the guard keeps the helper safe for a caller - // that does not. yield 'empty array is a single record' => [[], FALSE]; } @@ -247,9 +244,9 @@ protected function invokeNormalize(AbstractHandler $handler, mixed $input): arra /** * Creates an AbstractHandler subclass with the main property injected. * - * Bypasses the constructor (which requires a full Drupal entity bootstrap) - * and sets 'mainProperty' directly via reflection - 'normalize()' only - * needs that one value. + * The constructor requires a full Drupal entity bootstrap, so it is + * bypassed. 'normalize()' needs only 'mainProperty', so only that value is + * set directly via reflection. */ protected function createHandler(string $main_property): AbstractHandler { $handler = (new \ReflectionClass(NormalizeTestHandler::class))->newInstanceWithoutConstructor(); diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/AddressHandlerTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/AddressHandlerTest.php index 32d281e6..a709a04d 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/AddressHandlerTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/AddressHandlerTest.php @@ -120,9 +120,6 @@ public function testNonHiddenOverridesAreIgnored(): void { ); } - /** - * Tests that excess numeric indices trigger an exception. - */ public function testTooManyNumericIndicesThrows(): void { $handler = $this->createHandlerWithSettings([ 'additionalName' => ['override' => 'hidden'], diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/EntityReferenceHandlerTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/EntityReferenceHandlerTest.php index 96297b6e..9e2b1502 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/EntityReferenceHandlerTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/EntityReferenceHandlerTest.php @@ -25,7 +25,7 @@ class EntityReferenceHandlerTest extends FieldHandlerUnitTestBase { /** - * Label -> id lookup the entity query stub returns matches against. + * Label-to-id index the entity query stub returns matches from. * * @var array */ diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/FieldClassifierTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/FieldClassifierTest.php index 27fa52e0..e4f98929 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/FieldClassifierTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/FieldClassifierTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase; /** - * Tests the classifier against all nine F-row categories. + * Tests the classifier against all 9 F-row categories. */ #[CoversClass(FieldClassifier::class)] #[Group('core')] @@ -130,7 +130,7 @@ public function testFieldIsBundleStorageBacked(): void { } /** - * Builds an entity-field-manager fixture with one field per F-row. + * Builds an entity-field-manager fixture with 1 field per F-row. */ protected function entityFieldManager(): EntityFieldManagerInterface { // Storage stubs for the hasCustomStorage() chain on base definitions. diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/FieldHandlerUnitTestBase.php b/tests/phpunit/src/Unit/Backend/Core/Field/FieldHandlerUnitTestBase.php index 8028cac0..b8476cba 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/FieldHandlerUnitTestBase.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/FieldHandlerUnitTestBase.php @@ -59,9 +59,9 @@ public function testExpand(mixed $input, mixed $expected, ?string $exception, ?s } } - // Only suppress PHP warnings on rows that expect an exception (e.g. - // 'file_get_contents()' raises a warning before the handler throws); - // success-path rows must not silently swallow unexpected warnings. + // Suppress PHP warnings only on rows that expect an exception, where + // 'file_get_contents()' can raise a warning before the handler throws. A + // success-path row must not suppress an unexpected warning. $result = $exception !== NULL ? @$handler->expand($input) : $handler->expand($input); diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/FileBackedHandlerTestBase.php b/tests/phpunit/src/Unit/Backend/Core/Field/FileBackedHandlerTestBase.php index 01760809..45f12b68 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/FileBackedHandlerTestBase.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/FileBackedHandlerTestBase.php @@ -7,11 +7,13 @@ /** * Base unit test for handlers that write uploads through 'file.repository'. * - * Supplies the test doubles the file, image and supported-image handlers share: - * a File entity exposing 'id()', the repository that returns one on write, and - * an entity type manager whose file storage answers URI lookups. Subclasses - * build their own container in 'setUp()' from these, registering only the - * services the handler under test reaches for. + * Supplies the test doubles the file, image and supported-image handlers + * share. The doubles are a File entity exposing 'id()', the repository that + * returns one on write, and an entity type manager whose file storage answers + * URI lookups. + * + * Subclasses build their own container in 'setUp()' from these, registering + * only the services the handler under test uses. */ abstract class FileBackedHandlerTestBase extends FieldHandlerUnitTestBase { diff --git a/tests/phpunit/src/Unit/Backend/Core/Field/NameHandlerTest.php b/tests/phpunit/src/Unit/Backend/Core/Field/NameHandlerTest.php index aa0ff9ff..9989769b 100644 --- a/tests/phpunit/src/Unit/Backend/Core/Field/NameHandlerTest.php +++ b/tests/phpunit/src/Unit/Backend/Core/Field/NameHandlerTest.php @@ -18,7 +18,7 @@ class NameHandlerTest extends FieldHandlerUnitTestBase { /** - * All six name components, all enabled (the module's default). + * All 6 name components, all enabled (the module's default). * * @var array */ @@ -117,9 +117,6 @@ public static function dataProviderExpand(): \Iterator { ]; } - /** - * Tests that positional indices skip components disabled on the field. - */ public function testNumericIndicesSkipDisabledComponents(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => TRUE, @@ -136,9 +133,6 @@ public function testNumericIndicesSkipDisabledComponents(): void { ); } - /** - * Tests that excess positional indices throw. - */ public function testTooManyNumericIndicesThrows(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => FALSE, @@ -155,9 +149,6 @@ public function testTooManyNumericIndicesThrows(): void { $handler->expand([['John', 'Doe', 'Extra']]); } - /** - * Tests that a named key targeting a disabled component throws. - */ public function testNamedKeyForDisabledComponentThrows(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => FALSE, @@ -174,9 +165,6 @@ public function testNamedKeyForDisabledComponentThrows(): void { $handler->expand([['given' => 'John', 'middle' => 'Q', 'family' => 'Doe']]); } - /** - * Tests that the shorthand throws when 'family' is disabled. - */ public function testShorthandThrowsWhenFamilyDisabled(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => TRUE, @@ -193,9 +181,6 @@ public function testShorthandThrowsWhenFamilyDisabled(): void { $handler->expand('Doe, John'); } - /** - * Tests that the shorthand throws when a given part is supplied but disabled. - */ public function testShorthandThrowsWhenGivenPartSuppliedButDisabled(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => FALSE, @@ -212,9 +197,6 @@ public function testShorthandThrowsWhenGivenPartSuppliedButDisabled(): void { $handler->expand('Doe, John'); } - /** - * Tests the family-only shorthand when 'given' is disabled. - */ public function testShorthandFamilyOnlyWhenGivenDisabled(): void { $handler = $this->createHandlerWithComponents([ NameHandler::COMPONENT_TITLE => FALSE, diff --git a/tests/phpunit/src/Unit/Backend/CoreLookupTest.php b/tests/phpunit/src/Unit/Backend/CoreLookupTest.php index cef4a6ae..4d409c73 100644 --- a/tests/phpunit/src/Unit/Backend/CoreLookupTest.php +++ b/tests/phpunit/src/Unit/Backend/CoreLookupTest.php @@ -29,9 +29,6 @@ class_exists(Core99Core::class), ); } - /** - * Tests that a version-specific Core class is preferred over the default. - */ public function testLookupPicksVersionOverride(): void { $backend = $this->createBackendWithVersion(99); $backend->setCoreFromVersion(); diff --git a/tests/phpunit/src/Unit/Backend/DrupalBackendCreationAliasesTest.php b/tests/phpunit/src/Unit/Backend/DrupalBackendCreationAliasesTest.php index 61072ae4..1c7d5431 100644 --- a/tests/phpunit/src/Unit/Backend/DrupalBackendCreationAliasesTest.php +++ b/tests/phpunit/src/Unit/Backend/DrupalBackendCreationAliasesTest.php @@ -17,8 +17,8 @@ /** * Tests creation-alias discovery on 'DrupalBackend'. * - * The backend delegates to its 'Core' instance; behaviour here pins the - * delegation contract without booting Drupal. + * The backend delegates to its 'Core' instance; the tests pin the delegation + * contract without booting Drupal. */ #[CoversClass(DrupalBackend::class)] #[Group('backends')] diff --git a/tests/phpunit/src/Unit/Backend/DrupalBackendDelegationTest.php b/tests/phpunit/src/Unit/Backend/DrupalBackendDelegationTest.php index dcd49b9e..8df4545b 100644 --- a/tests/phpunit/src/Unit/Backend/DrupalBackendDelegationTest.php +++ b/tests/phpunit/src/Unit/Backend/DrupalBackendDelegationTest.php @@ -27,9 +27,6 @@ #[Group('drupal')] class DrupalBackendDelegationTest extends TestCase { - /** - * Tests that 'getCore()' returns the injected core instance. - */ public function testGetCoreReturnsInjectedCore(): void { $core = $this->createMock(CoreInterface::class); $backend = $this->createBackendWithCore($core); @@ -37,9 +34,6 @@ public function testGetCoreReturnsInjectedCore(): void { $this->assertSame($core, $backend->getCore()); } - /** - * Tests that 'getRandom()' delegates to the core. - */ public function testGetRandomDelegatesToCore(): void { $random = new Random(); $core = $this->createMock(CoreInterface::class); @@ -67,9 +61,6 @@ public function testBootstrapDelegatesToCore(): void { /** * Tests that 'setCore()' assigns the injected instance verbatim. - * - * The class name and namespace of the injected core are not inspected - - * any implementation of 'CoreInterface' is accepted. */ public function testSetCoreAssignsInjectedInstance(): void { $backend = $this->createBackendWithCore($this->createMock(CoreInterface::class)); @@ -80,9 +71,6 @@ public function testSetCoreAssignsInjectedInstance(): void { $this->assertSame($custom, $backend->getCore()); } - /** - * Tests that 'login()' delegates to an auth-capable core. - */ public function testLoginDelegatesToAuthCapableCore(): void { $stub = new EntityStub('user'); $core = $this->createMock(AuthCapableCoreInterface::class); @@ -93,9 +81,6 @@ public function testLoginDelegatesToAuthCapableCore(): void { $backend->login($stub); } - /** - * Tests that 'logout()' delegates to an auth-capable core. - */ public function testLogoutDelegatesToAuthCapableCore(): void { $core = $this->createMock(AuthCapableCoreInterface::class); $core->expects($this->once())->method('logout'); @@ -105,9 +90,6 @@ public function testLogoutDelegatesToAuthCapableCore(): void { $backend->logout(); } - /** - * Tests that 'login()' throws when the core does not support auth. - */ public function testLoginThrowsWithNonAuthCore(): void { $backend = $this->createBackendWithCore($this->createMock(CoreInterface::class)); @@ -117,9 +99,6 @@ public function testLoginThrowsWithNonAuthCore(): void { $backend->login(new EntityStub('user')); } - /** - * Tests that 'logout()' throws when the core does not support auth. - */ public function testLogoutThrowsWithNonAuthCore(): void { $backend = $this->createBackendWithCore($this->createMock(CoreInterface::class)); @@ -130,15 +109,15 @@ public function testLogoutThrowsWithNonAuthCore(): void { } /** - * Tests that every delegating method forwards to the matching core method. - * - * @param string $backend_method - * The 'DrupalBackend' method to invoke. - * @param array $args - * Positional arguments. - * @param string $core_method - * The expected core method to be invoked with the same args. - */ + * Tests that every delegating method forwards to the matching core method. + * + * @param string $backend_method + * The 'DrupalBackend' method to invoke. + * @param array $args + * Positional arguments. + * @param string $core_method + * The expected core method to be invoked with the same args. + */ #[DataProvider('dataProviderForwardsToCore')] public function testForwardsToCore(string $backend_method, array $args, string $core_method): void { $core = $this->createMock(CoreInterface::class); diff --git a/tests/phpunit/src/Unit/Backend/DrupalBackendTest.php b/tests/phpunit/src/Unit/Backend/DrupalBackendTest.php index b7f44815..e2b6026a 100644 --- a/tests/phpunit/src/Unit/Backend/DrupalBackendTest.php +++ b/tests/phpunit/src/Unit/Backend/DrupalBackendTest.php @@ -51,11 +51,11 @@ public function testImplementsDrupalBackendInterface(): void { } /** - * Tests that DrupalBackend advertises every capability. - * - * @param string $capability_class - * The capability interface name. - */ + * Tests that DrupalBackend advertises every capability. + * + * @param string $capability_class + * The capability interface name. + */ #[DataProvider('dataProviderImplementsCapability')] public function testImplementsCapability(string $capability_class): void { $this->assertTrue(is_subclass_of(DrupalBackend::class, $capability_class), sprintf( @@ -82,9 +82,6 @@ public static function dataProviderImplementsCapability(): \Iterator { /** * Tests that 'detectMajorVersion()' rejects an unparseable version string. - * - * Uses a fixture subclass to inject a non-numeric version value without - * touching the real '\Drupal::VERSION' constant. */ public function testDetectMajorVersionRejectsNonNumeric(): void { $this->expectException(BootstrapException::class); @@ -94,9 +91,6 @@ public function testDetectMajorVersionRejectsNonNumeric(): void { new FakeVersionDrupalBackend(self::DRUPAL_ROOT, 'default'); } - /** - * Tests that 'detectMajorVersion()' rejects pre-11 versions. - */ public function testDetectMajorVersionRejectsPre11(): void { $this->expectException(BootstrapException::class); $this->expectExceptionMessageMatches('/Unsupported Drupal core version/'); @@ -141,9 +135,6 @@ public function testDetectMajorVersionRejectsPartialRoot(string $present, string } } - /** - * Data provider for 'testDetectMajorVersionRejectsPartialRoot()'. - */ public static function dataProviderDetectMajorVersionRejectsPartialRoot(): \Iterator { yield 'bootstrap include missing' => ['/autoload.php', '/core/includes/bootstrap.inc']; yield 'autoloader missing' => ['/core/includes/bootstrap.inc', '/autoload.php']; diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendCreationAliasesTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendCreationAliasesTest.php index 19195047..ea879821 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendCreationAliasesTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendCreationAliasesTest.php @@ -14,8 +14,6 @@ /** * Tests creation-alias discovery on 'DrushBackend'. - * - * Drush owns 'RolesAlias' (post-create) and nothing else by default. */ #[CoversClass(DrushBackend::class)] #[Group('backends')] @@ -30,9 +28,6 @@ public function testImplementsCreationAliasCapability(): void { $this->assertContains(CreationAliasCapabilityInterface::class, (array) class_implements(DrushBackend::class)); } - /** - * Tests that 'roles' is registered for the user entity type. - */ public function testRolesAliasRegisteredForUser(): void { $backend = new DrushBackend('test-alias'); @@ -52,18 +47,12 @@ public function testNoContentAliasesByDefault(): void { $this->assertSame([], $backend->getCreationAliases('taxonomy_term')); } - /** - * Tests that 'getCreationAliases()' returns '[]' for unknown entity types. - */ public function testGetCreationAliasesReturnsEmptyForUnknown(): void { $backend = new DrushBackend('test-alias'); $this->assertSame([], $backend->getCreationAliases('unknown_type')); } - /** - * Tests that re-registering an alias replaces the previous instance. - */ public function testRegisterCreationAliasReplacesByName(): void { $backend = new DrushBackend('test-alias'); diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php index c869ee16..8fdf771b 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendMethodsTest.php @@ -42,9 +42,6 @@ protected function setUp(): void { } } - /** - * Tests that 'bootstrap()' flips the bootstrapped flag. - */ public function testBootstrapMarksAsBootstrapped(): void { $backend = $this->createBackend(); @@ -53,18 +50,12 @@ public function testBootstrapMarksAsBootstrapped(): void { $this->assertTrue($backend->isBootstrapped()); } - /** - * Tests that 'getRandom()' returns the random generator. - */ public function testGetRandomReturnsGenerator(): void { $backend = $this->createBackend(); $this->assertInstanceOf(Random::class, $backend->getRandom()); } - /** - * Tests that 'setArguments()' and 'getArguments()' are symmetrical. - */ public function testArgumentsRoundTrip(): void { $backend = $this->createBackend(); $backend->setArguments('--uri=http://example.com'); @@ -72,9 +63,6 @@ public function testArgumentsRoundTrip(): void { $this->assertSame('--uri=http://example.com', $backend->getArguments()); } - /** - * Tests that 'processBatch()' is a no-op. - */ public function testProcessBatchIsNoop(): void { $backend = $this->createBackend(); $backend->processBatch(); @@ -82,9 +70,6 @@ public function testProcessBatchIsNoop(): void { $this->addToAssertionCount(1); } - /** - * Tests 'cacheClear()' rebuilds the cache. - */ public function testCacheClearRebuilds(): void { $backend = $this->createBackend(); @@ -95,9 +80,6 @@ public function testCacheClearRebuilds(): void { $this->assertContains('cache:rebuild', $commands); } - /** - * Tests that 'cacheClearStatic()' is a no-op. - */ public function testCacheClearStaticIsNoop(): void { $backend = $this->createBackend(); $backend->cacheClearStatic(); @@ -105,9 +87,6 @@ public function testCacheClearStaticIsNoop(): void { $this->addToAssertionCount(1); } - /** - * Tests that '__call()' forwards unknown methods through 'drush()'. - */ public function testMagicCallForwardsToDrush(): void { $backend = $this->createBackend(); $backend->drushResponse = 'magic-output'; @@ -119,9 +98,6 @@ public function testMagicCallForwardsToDrush(): void { $this->assertSame('status', $backend->invocations[0]['command']); } - /** - * Tests 'userCreate()' applies roles when the user object declares them. - */ public function testUserCreateWithRolesInvokesRoleAssignment(): void { $backend = $this->createBackend(); $backend->drushResponse = "User ID : 7\nUser name : bob\n"; @@ -140,9 +116,6 @@ public function testUserCreateWithRolesInvokesRoleAssignment(): void { $this->assertSame(2, array_count_values($commands)['user-add-role'] ?? 0); } - /** - * Tests 'userCreate()' rejects a response carrying no user id. - */ public function testUserCreateThrowsWhenDrushReportsNoUserId(): void { $backend = $this->createBackend(); $backend->drushResponse = "Nothing resembling a user id.\n"; @@ -173,7 +146,7 @@ public function testCacheClearDrushOnlySkipsRebuild(): void { } /** - * Tests that 'drush()' actually spawns the configured binary. + * Tests that 'drush()' spawns the configured binary. * * Uses 'echo' as the binary so the test runs deterministically without * requiring a real Drush install. Echo prints the assembled command back on @@ -194,9 +167,6 @@ public function testDrushExecutesBinaryAndReturnsOutput(): void { $this->assertStringContainsString('version', $result); } - /** - * Tests that 'drush()' always emits the '--no-ansi' flag. - */ public function testDrushAlwaysEmitsNoAnsiFlag(): void { $echo = $this->resolveSystemBinary('echo'); if ($echo === NULL) { @@ -211,7 +181,7 @@ public function testDrushAlwaysEmitsNoAnsiFlag(): void { } /** - * Tests that 'resolveProjectDrush()' picks up COMPOSER_BIN_DIR first. + * Tests that 'resolveProjectDrush()' prefers 'COMPOSER_BIN_DIR'. */ public function testResolveProjectDrushPrefersComposerBin(): void { $temp_dir = self::TEMP_ROOT . '/drush-backend-test-' . uniqid(); @@ -231,9 +201,6 @@ public function testResolveProjectDrushPrefersComposerBin(): void { } } - /** - * Tests that 'resolveProjectDrush()' falls back to 'vendor/bin/drush'. - */ public function testResolveProjectDrushFallsBackToVendorBin(): void { $temp_dir = self::TEMP_ROOT . '/drush-backend-cwd-' . uniqid(); mkdir($temp_dir . '/vendor/bin', 0777, TRUE); @@ -287,9 +254,6 @@ public function testParseArguments(array $options, array $expected): void { $this->assertSame($expected, ArgumentsExposingDrushBackend::callParseArguments($options)); } - /** - * Data provider for 'testParseArguments()'. - */ public static function dataProviderParseArguments(): \Iterator { yield 'empty' => [[], []]; yield 'single flag' => [['yes' => NULL], ['--yes']]; @@ -313,9 +277,6 @@ public function testParseArgumentsRejectsName(string $name): void { ArgumentsExposingDrushBackend::callParseArguments([$name => 'value']); } - /** - * Data provider for 'testParseArgumentsRejectsName()'. - */ public static function dataProviderParseArgumentsRejectsName(): \Iterator { yield 'space' => ['two words']; yield 'leading dash' => ['-format']; @@ -382,11 +343,11 @@ public static function dataProviderInvokesDrush(): \Iterator { } /** - * Tests that a config write hands Drush a format it actually parses. + * Tests that a config write requests an input format Drush parses. * - * 'config:set' parses its value only under '--input-format=yaml'; any other - * value is stored verbatim, so a JSON payload would land as its own encoding - * rather than as the value it encodes. + * 'config:set' parses its value only under '--input-format=yaml'; under any + * other format the value is stored verbatim, so a JSON payload would be + * stored as its own encoding, not as the value it encodes. */ public function testConfigSetRequestsParsedInputFormat(): void { $backend = $this->createBackend(); @@ -432,9 +393,6 @@ public function testUnwrapsEnvelope(string $method, array $args, string $drush_r $this->assertSame($expected, $backend->{$method}(...$args)); } - /** - * Data provider for testUnwrapsEnvelope(). - */ public static function dataProviderUnwrapsEnvelope(): \Iterator { yield 'config key read unwraps the name:key entry' => [ 'configGet', @@ -487,7 +445,8 @@ public function testConfigDeleteOfMissingObjectIsNoOp(): void { * Tests that a module lookup matches the machine name exactly. * * The 'pm:list' filter matches any substring of a name, so a listing that - * only holds a longer neighbour must not report the module as present. + * only holds a module with a longer name must not report the module as + * present. */ public function testModuleLookupMatchesTheExactName(): void { $backend = $this->createBackend(); @@ -503,7 +462,7 @@ public function testModuleLookupMatchesTheExactName(): void { } /** - * Tests that a failed write puts the configuration object back. + * Tests that a failed write restores the configuration object. * * The delete and the write are separate commands, so a write that fails * after the delete would otherwise leave the object missing instead of @@ -512,7 +471,6 @@ public function testModuleLookupMatchesTheExactName(): void { public function testConfigSetDataRestoresTheObjectWhenTheWriteFails(): void { $backend = $this->createBackend(); $backend->drushResponse = '{"name":"Original"}'; - // Fail the first 'config:set' and let the restoring one through. $backend->drushFailures['config:set'] = 1; try { @@ -545,10 +503,10 @@ public function testConfigSetDataReplacesRatherThanMerges(): void { } /** - * Tests that an object holding nothing is written without being deleted. + * Tests that an empty object is written without being deleted. * * Deleting it would drop no key and leave nothing to restore from, because - * 'config:set' refuses to write an empty object back. + * 'config:set' rejects an empty object. */ public function testConfigSetDataKeepsAnEmptyObjectInPlace(): void { $backend = $this->createBackend(); diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendResultTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendResultTest.php index 9d46fa10..1be05b05 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendResultTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendResultTest.php @@ -41,9 +41,6 @@ public function testDrushResultExposesValues(int $exit_code, string $output, str $this->assertSame($error_output, $result->errorOutput); } - /** - * Data provider for 'testDrushResultExposesValues()'. - */ public static function dataProviderDrushResultExposesValues(): \Iterator { yield 'success with stdout' => [0, 'the output', '']; yield 'failure with stderr' => [1, '', 'boom']; @@ -75,18 +72,12 @@ public function testDrushResultMapsProcess(?int $exit_code, string $output, stri $this->assertSame($error_output, $result->errorOutput); } - /** - * Data provider for 'testDrushResultMapsProcess()'. - */ public static function dataProviderDrushResultMapsProcess(): \Iterator { yield 'success' => [0, 'stdout text', '', 0]; yield 'failure with stderr' => [2, '', 'stderr text', 2]; yield 'signalled process maps null exit to one' => [NULL, '', '', 1]; } - /** - * Tests that 'drush()' throws and carries stderr when the command fails. - */ public function testDrushThrowsWithErrorOutputOnFailure(): void { $backend = new ProcessStubDrushBackend('alias'); $backend->stubProcess = $this->mockProcess(2, '', 'the failure reason'); @@ -115,9 +106,6 @@ public function testDrushStdoutElseStderrFallback(string $output, string $error_ $this->assertSame($expected, $backend->drush('status')); } - /** - * Data provider for 'testDrushStdoutElseStderrFallback()'. - */ public static function dataProviderDrushStdoutElseStderrFallback(): \Iterator { yield 'stdout present' => ['hello', 'ignored stderr', 'hello']; yield 'empty stdout falls back to stderr' => ['', 'stderr fallback', 'stderr fallback']; diff --git a/tests/phpunit/src/Unit/Backend/DrushBackendTest.php b/tests/phpunit/src/Unit/Backend/DrushBackendTest.php index 1be0ec31..47fa2adb 100644 --- a/tests/phpunit/src/Unit/Backend/DrushBackendTest.php +++ b/tests/phpunit/src/Unit/Backend/DrushBackendTest.php @@ -67,8 +67,8 @@ public function testWithNeither(): void { } /** - * Tests 'parseUserId()' correctly extracts UID from drush output. - */ + * Tests that 'parseUserId()' extracts the UID from Drush output. + */ #[DataProvider('dataProviderParseUserId')] public function testParseUserId(string $drush_output, ?int $expected): void { $backend = new TestDrushBackend('alias'); @@ -76,9 +76,6 @@ public function testParseUserId(string $drush_output, ?int $expected): void { $this->assertSame($expected, $result); } - /** - * Data provider for testParseUserId(). - */ public static function dataProviderParseUserId(): \Iterator { yield 'legacy key-value format' => [ "User ID : 550895\nUser name : test\n", diff --git a/tests/phpunit/src/Unit/Backend/Entity/EntityStubTest.php b/tests/phpunit/src/Unit/Backend/Entity/EntityStubTest.php index 7a1c4ff1..4b7d0d14 100644 --- a/tests/phpunit/src/Unit/Backend/Entity/EntityStubTest.php +++ b/tests/phpunit/src/Unit/Backend/Entity/EntityStubTest.php @@ -17,9 +17,6 @@ #[Group('entity')] class EntityStubTest extends TestCase { - /** - * Tests that the constructor pins entity type and bundle. - */ public function testConstructorPinsTypeAndBundle(): void { $stub = new EntityStub('node', 'article'); @@ -27,9 +24,6 @@ public function testConstructorPinsTypeAndBundle(): void { $this->assertSame('article', $stub->getBundle()); } - /** - * Tests that the constructor accepts an initial values bag. - */ public function testConstructorAcceptsInitialValues(): void { $stub = new EntityStub('node', 'article', ['title' => 'Hello']); @@ -47,9 +41,6 @@ public function testBundleDefaultsToNull(): void { $this->assertNull($stub->getBundle()); } - /** - * Tests that 'getValue()' returns the supplied default for unset keys. - */ public function testGetValueReturnsDefaultWhenAbsent(): void { $stub = new EntityStub('node', 'article'); @@ -57,9 +48,6 @@ public function testGetValueReturnsDefaultWhenAbsent(): void { $this->assertSame('fallback', $stub->getValue('title', 'fallback')); } - /** - * Tests that 'setValue()' returns $this for chaining. - */ public function testSetValueIsChainable(): void { $stub = new EntityStub('node', 'article'); @@ -72,10 +60,6 @@ public function testSetValueIsChainable(): void { /** * Tests that 'hasValue()' is true even when the stored value is NULL. - * - * Pinned alongside the 'getValue()' assertion so a regression that replaces - * 'array_key_exists()' with the null-coalescing operator surfaces here - * rather than downstream. */ public function testHasValueDistinguishesNullFromAbsent(): void { $stub = new EntityStub('node', 'article'); @@ -87,9 +71,6 @@ public function testHasValueDistinguishesNullFromAbsent(): void { $this->assertSame('fallback', $stub->getValue('promote', 'fallback'), 'Unset key falls back to the supplied default.'); } - /** - * Tests that 'removeValue()' deletes a key from the bag. - */ public function testRemoveValueDeletesKey(): void { $stub = new EntityStub('node', 'article', ['title' => 'Hello']); @@ -99,9 +80,6 @@ public function testRemoveValueDeletesKey(): void { $this->assertSame([], $stub->getValues()); } - /** - * Tests that 'setValues()' replaces the bag wholesale. - */ public function testSetValuesReplacesBag(): void { $stub = new EntityStub('node', 'article', ['title' => 'Old']); @@ -111,9 +89,6 @@ public function testSetValuesReplacesBag(): void { $this->assertSame(1, $stub->getValue('promote')); } - /** - * Tests that 'isSaved()' flips after 'markSaved()'. - */ public function testIsSavedFlipsAfterMarkSaved(): void { $stub = new EntityStub('node', 'article'); @@ -124,9 +99,6 @@ public function testIsSavedFlipsAfterMarkSaved(): void { $this->assertTrue($stub->isSaved()); } - /** - * Tests that 'getSavedEntity()' returns the supplied entity. - */ public function testGetSavedEntityReturnsAttachedObject(): void { $entity = (object) ['id' => 7]; $stub = new EntityStub('node', 'article'); @@ -135,9 +107,6 @@ public function testGetSavedEntityReturnsAttachedObject(): void { $this->assertSame($entity, $stub->getSavedEntity()); } - /** - * Tests that 'getSavedEntity()' throws on an unsaved stub. - */ public function testGetSavedEntityThrowsWhenUnsaved(): void { $stub = new EntityStub('node', 'article'); @@ -168,18 +137,12 @@ public function id(): int { $this->assertSame(42, $stub->getId()); } - /** - * Tests that 'getId()' returns NULL when the stub is not saved. - */ public function testGetIdReturnsNullWhenUnsaved(): void { $stub = new EntityStub('node', 'article'); $this->assertNull($stub->getId()); } - /** - * Tests that 'getId()' returns NULL when the saved entity has no 'id()'. - */ public function testGetIdReturnsNullWhenSavedEntityHasNoIdMethod(): void { $stub = new EntityStub('node', 'article'); $stub->markSaved((object) ['identifier' => 'x']); @@ -187,9 +150,6 @@ public function testGetIdReturnsNullWhenSavedEntityHasNoIdMethod(): void { $this->assertNull($stub->getId()); } - /** - * Tests that the bundle key defaults to 'type' and is mutable. - */ public function testBundleKeyDefaultsToTypeAndIsMutable(): void { $stub = new EntityStub('taxonomy_term', 'tags'); @@ -202,9 +162,6 @@ public function testBundleKeyDefaultsToTypeAndIsMutable(): void { $this->assertSame('vid', $stub->getBundleKey()); } - /** - * Tests that the stub implements the documented interface. - */ public function testImplementsInterface(): void { $this->assertInstanceOf(EntityStubInterface::class, new EntityStub('node')); } diff --git a/tests/phpunit/src/Unit/Backend/Exception/UnsupportedBackendActionExceptionTest.php b/tests/phpunit/src/Unit/Backend/Exception/UnsupportedBackendActionExceptionTest.php index 7e96fc4e..3c2dba75 100644 --- a/tests/phpunit/src/Unit/Backend/Exception/UnsupportedBackendActionExceptionTest.php +++ b/tests/phpunit/src/Unit/Backend/Exception/UnsupportedBackendActionExceptionTest.php @@ -29,9 +29,6 @@ public function testMessageFormatting(): void { $this->assertSame(sprintf('Action %s is not supported.', $backend_class), $exception->getMessage()); } - /** - * Tests that the backend is accessible via getBackend(). - */ public function testGetBackendReturnsConstructorArgument(): void { $backend = $this->createMock(BackendInterface::class); @@ -40,9 +37,6 @@ public function testGetBackendReturnsConstructorArgument(): void { $this->assertSame($backend, $exception->getBackend()); } - /** - * Tests that code and previous exception are propagated to the parent. - */ public function testCodeAndPreviousArePropagated(): void { $backend = $this->createMock(BackendInterface::class); $previous = new \RuntimeException('root cause'); diff --git a/tests/phpunit/src/Unit/Backend/Fixtures/RecordingDrushBackend.php b/tests/phpunit/src/Unit/Backend/Fixtures/RecordingDrushBackend.php index 0f23e8fb..660c4d07 100644 --- a/tests/phpunit/src/Unit/Backend/Fixtures/RecordingDrushBackend.php +++ b/tests/phpunit/src/Unit/Backend/Fixtures/RecordingDrushBackend.php @@ -10,7 +10,7 @@ /** * Subclass of 'DrushBackend' that records every Drush invocation. * - * Both entry points are stubbed, because a backend method reaches for + * Both entry points are stubbed, because a backend method calls * 'drushResult()' when a non-zero exit is an answer rather than a failure. */ class RecordingDrushBackend extends DrushBackend { @@ -33,11 +33,10 @@ class RecordingDrushBackend extends DrushBackend { public int $drushExitCode = 0; /** - * Commands 'drush()' raises on, as a command name to remaining-failure count. + * Remaining failure counts keyed by the command name 'drush()' throws on. * - * A count lets a test fail the first call of a command and let a later one - * through, which is what a backend method recovering from a failed write - * needs. + * A count lets a test fail the first call of a command and let a later + * call succeed. * * @var array */ diff --git a/tests/phpunit/src/Unit/Behat/Config/GroupNameTest.php b/tests/phpunit/src/Unit/Behat/Config/GroupNameTest.php index 3ee42c06..a5d25dda 100644 --- a/tests/phpunit/src/Unit/Behat/Config/GroupNameTest.php +++ b/tests/phpunit/src/Unit/Behat/Config/GroupNameTest.php @@ -62,9 +62,9 @@ public function testNameWithoutTheTraitSuffixIsReadAsIs(): void { /** * Tests that a run of capitals reads as one word. * - * A trait carrying an acronym has to derive the same group from its name as - * from the prefix its declaring method carries, or the option that switches - * it off cannot be reached by the trait name. + * An acronym trait has to derive the same group from its name and from its + * method prefix. Otherwise the option that switches it off cannot be + * reached by the trait name. */ public function testAcronymDerivesOneGroupFromBothNames(): void { $this->assertSame('api_client', GroupName::fromTraitName('APIClientTrait')); diff --git a/tests/phpunit/src/Unit/Behat/Config/TagOverridesTest.php b/tests/phpunit/src/Unit/Behat/Config/TagOverridesTest.php index 707115fc..ed1d9817 100644 --- a/tests/phpunit/src/Unit/Behat/Config/TagOverridesTest.php +++ b/tests/phpunit/src/Unit/Behat/Config/TagOverridesTest.php @@ -69,8 +69,8 @@ public static function dataProviderSkipTagBinding(): \Iterator { yield 'a single word group names its trait' => ['cache', ['behat-steps-skip:CacheTrait'], FALSE]; yield 'a tag that is not a skip tag leaves it alone' => ['big_pipe', ['javascript'], TRUE]; - // The tag carries the trait's own name, which the group cannot be - // converted back into, so every spelling deriving the group matches. + // The group cannot be converted back into the trait name the tag carries, + // so every spelling deriving the group matches. yield 'an acronym trait name switches its group off' => ['api_client', ['behat-steps-skip:APIClientTrait'], FALSE]; yield 'the same group spelled without the acronym also matches' => ['api_client', ['behat-steps-skip:ApiClientTrait'], FALSE]; } diff --git a/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php b/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php index c391acab..b2d032e3 100644 --- a/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php +++ b/tests/phpunit/src/Unit/Behat/Config/TraitOptionResolverTest.php @@ -176,7 +176,7 @@ public function testContextDeclaringNothingNamesWhatItAccepts(): void { } /** - * Tests that the steps section tolerates what a context cannot serve. + * Tests that the steps section may hold entries a context does not declare. * * @param array $steps * The extension's steps section. diff --git a/tests/phpunit/src/Unit/Behat/Context/Attribute/HookAttributeReaderTest.php b/tests/phpunit/src/Unit/Behat/Context/Attribute/HookAttributeReaderTest.php index c50be4a7..f65d0431 100644 --- a/tests/phpunit/src/Unit/Behat/Context/Attribute/HookAttributeReaderTest.php +++ b/tests/phpunit/src/Unit/Behat/Context/Attribute/HookAttributeReaderTest.php @@ -41,9 +41,8 @@ public function testAnInstanceHookResolvesToItsContextMethod(): void { $this->assertCount(1, $callees); $this->assertInstanceOf(AfterNodeCreate::class, $callees[0]); - // Behat 3 takes the '[class, method]' pair and Behat 4 wraps an instance - // method in a late-bound callable. The assertion targets the method the - // callee resolves to, not the shape it is carried in. + // The assertion targets the method the callee resolves to, not the shape + // of its callable. $reflection = $callees[0]->getReflection(); $this->assertInstanceOf(\ReflectionMethod::class, $reflection); diff --git a/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php b/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php index 080b418a..70ff0f77 100644 --- a/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php +++ b/tests/phpunit/src/Unit/Behat/Context/ContextConfigTest.php @@ -145,7 +145,7 @@ public static function dataProviderSkipTag(): \Iterator { yield 'a hook tag does not skip the hook' => ['SampleTrait', ['behat-steps-skip:sampleBeforeScenario'], [], FALSE]; yield 'another trait tag does not skip the hook' => ['SampleTrait', ['behat-steps-skip:SampleExtraTrait'], [], FALSE]; - // 'SampleExtraTrait' starts with 'Sample', and each trait keeps to its own + // 'SampleExtraTrait' starts with 'Sample', and each trait maps to its own // group in both directions. yield 'a disabled group skips its own trait' => ['SampleExtraTrait', [], ['sample_extra' => ['enabled' => FALSE]], TRUE]; yield 'a disabled group leaves a trait extending its name running' => ['SampleExtraTrait', [], ['sample' => ['enabled' => FALSE]], FALSE]; diff --git a/tests/phpunit/src/Unit/Behat/Context/WebContextTest.php b/tests/phpunit/src/Unit/Behat/Context/WebContextTest.php index 17d5ba7a..9bf0ea46 100644 --- a/tests/phpunit/src/Unit/Behat/Context/WebContextTest.php +++ b/tests/phpunit/src/Unit/Behat/Context/WebContextTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\CoversClass; /** - * Tests the guard against a suite registering two web vocabularies. + * Tests the guard against a suite registering 2 web vocabularies. */ #[CoversClass(WebContext::class)] class WebContextTest extends UnitTestCase { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/BareMinkContext.php b/tests/phpunit/src/Unit/Behat/Fixtures/BareMinkContext.php index c89fdf8f..3394c5f6 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/BareMinkContext.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/BareMinkContext.php @@ -17,8 +17,7 @@ * Composes every trait that runs on Mink's own base context. * * A trait listed here is one a project can compose without the library's own - * context. 'ContextCompositionTest' holds the list against the annotations - * in the source. + * context. */ class BareMinkContext extends RawMinkContext { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/DuplicateOptionConfigContext.php b/tests/phpunit/src/Unit/Behat/Fixtures/DuplicateOptionConfigContext.php index 4013f902..ac21fa06 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/DuplicateOptionConfigContext.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/DuplicateOptionConfigContext.php @@ -16,7 +16,7 @@ class DuplicateOptionConfigContext extends WebRawContext { * Declares the same option name twice. * * @return array - * Two options sharing a name. + * 2 options sharing a name. */ protected function duplicateOptionConfigSchema(): array { return [ diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/ForeignMinkExtension.php b/tests/phpunit/src/Unit/Behat/Fixtures/ForeignMinkExtension.php index dd102914..d8afee71 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/ForeignMinkExtension.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/ForeignMinkExtension.php @@ -14,7 +14,7 @@ * Extension under the 'mink' key that is not Mink's own. * * It accepts driver factories the way Mink's extension does and records each - * one, so a test can tell whether a factory was registered with it. + * one. */ class ForeignMinkExtension implements Extension { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/HookedContext.php b/tests/phpunit/src/Unit/Behat/Fixtures/HookedContext.php index 3f7bbb02..ed3c8dd3 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/HookedContext.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/HookedContext.php @@ -18,7 +18,7 @@ class HookedContext implements Context { /** - * Static hook, which the reader can pass as a plain callable pair. + * Static hook, callable in its pair form. */ #[BeforeNodeCreate] public static function beforeNode(BeforeNodeCreateScope $scope): void { @@ -39,7 +39,7 @@ public static function filtered(): void { } /** - * Method carrying two entity hooks at once. + * Method carrying 2 entity hooks at once. */ #[BeforeNodeCreate] #[AfterNodeCreate] diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/PrerequisiteReaderHost.php b/tests/phpunit/src/Unit/Behat/Fixtures/PrerequisiteReaderHost.php index 20da093c..29e5e2da 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/PrerequisiteReaderHost.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/PrerequisiteReaderHost.php @@ -5,7 +5,7 @@ namespace DrevOps\BehatSteps\Tests\Unit\Behat\Fixtures; /** - * Plain class composing the traits the reader's failure and cache tests read. + * Plain class composing the counted and malformed prerequisite traits. */ class PrerequisiteReaderHost { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/SampleExtraConfigTrait.php b/tests/phpunit/src/Unit/Behat/Fixtures/SampleExtraConfigTrait.php index de35be3e..5f132354 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/SampleExtraConfigTrait.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/SampleExtraConfigTrait.php @@ -8,9 +8,6 @@ /** * Trait whose group name extends another switchable group's. - * - * 'sample_extra' starts with 'sample', and both groups declare a switch, so - * each has to resolve to its own group and never to the other. */ trait SampleExtraConfigTrait { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/ThrowingHookReader.php b/tests/phpunit/src/Unit/Behat/Fixtures/ThrowingHookReader.php index 2af44b2e..1133b394 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/ThrowingHookReader.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/ThrowingHookReader.php @@ -10,9 +10,6 @@ /** * Environment reader offering one hook whose callable throws. - * - * Lets a test drive the branch where the dispatcher collects an exception - * instead of raising it. */ class ThrowingHookReader implements EnvironmentReader { diff --git a/tests/phpunit/src/Unit/Behat/Fixtures/UnmappedHook.php b/tests/phpunit/src/Unit/Behat/Fixtures/UnmappedHook.php index bc2b8905..765257d8 100644 --- a/tests/phpunit/src/Unit/Behat/Fixtures/UnmappedHook.php +++ b/tests/phpunit/src/Unit/Behat/Fixtures/UnmappedHook.php @@ -10,8 +10,7 @@ /** * Hook attribute the reader has no call class for. * - * A project can declare its own attribute against the marker interface, which - * the reader must skip rather than fail on. + * A project can declare its own attribute against the marker interface. */ #[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] final class UnmappedHook implements DrupalHookInterface { diff --git a/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php b/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php index 42161237..7c14be63 100644 --- a/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php +++ b/tests/phpunit/src/Unit/Behat/Listener/BackendListenerTest.php @@ -136,12 +136,6 @@ public static function dataProviderBackendOrder(): \Iterator { ]; } - /** - * Tests that an example's outline tags and table tags rank together. - * - * Gherkin merges the outline's tags and the 'Examples:' table's tags into the - * example's own list, outline first. - */ public function testExampleRanksOutlineAndTableTagsTogether(): void { $table = new ExampleTableNode([1 => ['name'], 2 => ['value']], 'Examples', ['backend:drush']); $outline = new OutlineNode('Outline', ['backend:blackbox'], [], $table, 'Scenario Outline', 2); @@ -262,9 +256,6 @@ public function testTheScenarioTagsArePublished(): void { $this->assertSame(['api', 'javascript', 'error'], $this->scenarioTags->getTags()); } - /** - * Tests that each scenario replaces the tags of the one before it. - */ public function testTheTagsOfOneScenarioDoNotLeakIntoTheNext(): void { $listener = new BackendListener($this->createMock(BackendRegistryInterface::class), $this->scenarioTags, self::BACKENDS); diff --git a/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php b/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php index ebec5ebb..223fd10a 100644 --- a/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php +++ b/tests/phpunit/src/Unit/Behat/Manager/AuthenticatorTest.php @@ -516,8 +516,8 @@ public function testLogInWaitsForLoggedInSelector(): void { $page->method('has')->willReturnCallback(function (string $selector, string $locator) use (&$call_count): bool { if ($locator === 'body.logged-in') { $call_count++; - // First two calls return FALSE (during wait loop and loggedIn check), - // then return TRUE. + // The first 2 calls return FALSE (during the wait loop and the + // loggedIn() check), then TRUE. return $call_count > 2; } return FALSE; @@ -609,7 +609,6 @@ public function testLogInFailsWithoutLoginWaitWhenSelectorDelayed(): void { // @phpstan-ignore method.notFound $session->method('getCurrentUrl')->willReturn('http://localhost/user/1'); - // No login_wait is configured. $authenticator = $this->createAuthenticator($session); $this->expectException(\Exception::class); diff --git a/tests/phpunit/src/Unit/Behat/Mink/BrowserCapabilityResolverTest.php b/tests/phpunit/src/Unit/Behat/Mink/BrowserCapabilityResolverTest.php index a87e8bd2..f3c830dd 100644 --- a/tests/phpunit/src/Unit/Behat/Mink/BrowserCapabilityResolverTest.php +++ b/tests/phpunit/src/Unit/Behat/Mink/BrowserCapabilityResolverTest.php @@ -33,7 +33,7 @@ class BrowserCapabilityResolverTest extends UnitTestCase { /** - * Tests that each driver resolves to the adapter speaking for it. + * Tests that each driver resolves to the adapter that supports it. * * @param class-string<\Behat\Mink\Driver\DriverInterface> $driver_class * The browser driver class to mock. @@ -49,9 +49,6 @@ public function testAdapterForDriver(string $driver_class, string $expected): vo $this->assertInstanceOf($expected, (new BrowserCapabilityResolver())->resolve($driver, CookieCapabilityInterface::class)); } - /** - * Data provider for testAdapterForDriver(). - */ public static function dataProviderAdapterForDriver(): \Iterator { yield 'browserkit' => [BrowserKitDriver::class, BrowserKitAdapter::class]; yield 'selenium2' => [Selenium2Driver::class, Selenium2Adapter::class]; @@ -86,12 +83,9 @@ public function testDeclaredCapabilities(string $driver_class, array $expected): $this->assertSame($expected, $actual); } - /** - * Data provider for testDeclaredCapabilities(). - */ public static function dataProviderDeclaredCapabilities(): \Iterator { - // A BrowserKit driver is an HTTP client rather than a browser: it carries - // cookies, lends its client and sets headers, but runs no script. + // A BrowserKit driver is an HTTP client, not a browser: it carries + // cookies, exposes its client and sets headers, but runs no script. yield 'browserkit' => [ BrowserKitDriver::class, [CookieCapabilityInterface::class, HttpClientCapabilityInterface::class, RequestHeaderCapabilityInterface::class], @@ -123,7 +117,7 @@ public function testUnknownDriverProvidesNothing(): void { } /** - * Tests that a driver providing one capability still refuses another. + * Tests that a driver providing 1 capability fails to resolve another. */ public function testResolveRefusesCapabilityTheDriverLacks(): void { $driver = $this->createMock(BrowserKitDriver::class); @@ -135,9 +129,6 @@ public function testResolveRefusesCapabilityTheDriverLacks(): void { $resolver->resolve($driver, JavascriptCapabilityInterface::class); } - /** - * Tests that a registered adapter outranks the shipped ones. - */ public function testRegisteredAdapterTakesPrecedence(): void { $driver = $this->createMock(BrowserKitDriver::class); $resolver = new BrowserCapabilityResolver(); @@ -150,9 +141,6 @@ public function testRegisteredAdapterTakesPrecedence(): void { $this->assertTrue($resolver->has($driver, JavascriptCapabilityInterface::class), 'A registered adapter supplies a capability the shipped one lacks.'); } - /** - * Tests that the adapter for a driver is built once and reused. - */ public function testAdapterIsReusedForTheSameDriver(): void { $driver = $this->createMock(BrowserKitDriver::class); $resolver = new BrowserCapabilityResolver(); @@ -166,8 +154,8 @@ public function testAdapterIsReusedForTheSameDriver(): void { /** * Tests that the Selenium driver still declares the methods Syn needs. * - * The adapter reaches these through reflection because the driver exposes no - * public equivalent, so an upstream rename has to fail here rather than in a + * The adapter reaches these through reflection because the driver exposes + * no public equivalent. An upstream rename has to fail here, not in a * scenario. */ public function testSeleniumStillDeclaresTheReflectedMethods(): void { @@ -183,11 +171,9 @@ public function testSeleniumStillDeclaresTheReflectedMethods(): void { /** * Skips the test when the browser driver package is not installed. * - * Both JavaScript drivers are suggested rather than required, and the Chrome - * extension pins Behat 3, so a Behat 4 install resolves without it. An - * adapter still loads and reports FALSE for a driver class that is absent, - * which is what keeps the resolver working; only a test naming the class - * directly needs the package present. + * An adapter still loads and its supports() reports FALSE for a driver + * class that is absent, so the resolver works without the package. Only a + * test naming the class directly needs the package present. * * @param string $driver_class * The browser driver class the test mocks. diff --git a/tests/phpunit/src/Unit/Behat/Mink/Fixtures/AnyDriverAdapter.php b/tests/phpunit/src/Unit/Behat/Mink/Fixtures/AnyDriverAdapter.php index 7b7cd429..bc9f3f77 100644 --- a/tests/phpunit/src/Unit/Behat/Mink/Fixtures/AnyDriverAdapter.php +++ b/tests/phpunit/src/Unit/Behat/Mink/Fixtures/AnyDriverAdapter.php @@ -18,9 +18,9 @@ /** * Declares every capability for any driver, including a mocked one. * - * Registered the way a consuming project registers an adapter for its own Mink - * driver, so a test reaching a step body past its capability gate does so - * through the same seam rather than around it. + * Registered the way a consuming project registers an adapter for its own + * Mink driver. A test then reaches a step body past its capability gate + * through the same seam. */ class AnyDriverAdapter extends BrowserAdapterBase implements CookieCapabilityInterface, HttpClientCapabilityInterface, JavascriptCapabilityInterface, KeyboardCapabilityInterface, RequestHeaderCapabilityInterface { diff --git a/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php b/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php index 355bbe51..caa0a836 100644 --- a/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Drupal/EntityLifecycleTraitTest.php @@ -730,7 +730,7 @@ public function testLoggedInDelegatesToTheAuthenticator(): void { * Builds an initialized context over the given backend. * * @param \DrevOps\BehatSteps\Backend\BackendInterface $backend - * The backend the registry hands out. + * The backend the registry returns. * @param \DrevOps\BehatSteps\Behat\Manager\UserRegistryInterface|null $user_registry * The user registry, when the test inspects it. * @param \DrevOps\BehatSteps\Behat\Manager\AuthenticatorInterface|null $authenticator @@ -740,9 +740,8 @@ public function testLoggedInDelegatesToTheAuthenticator(): void { */ protected function createContext(BackendInterface $backend, ?UserRegistryInterface $user_registry = NULL, ?AuthenticatorInterface $authenticator = NULL, ?HookDispatcher $dispatcher = NULL): TestableRawContext { $environment = $this->createMock(Environment::class); - // A real environment binds a callee to the context instance it holds. The - // fixture hooks are static, so the callee's own callable is enough for - // the dispatcher to invoke them. + // The fixture hooks are static, so the callee's own callable is enough + // for the dispatcher to invoke them. $environment->method('bindCallee')->willReturnCallback(static fn(Callee $callee): mixed => $callee->getCallable()); $backend_registry = new BackendRegistry(['test' => $backend]); diff --git a/tests/phpunit/src/Unit/Helper/Web/RequestHeadersTraitTest.php b/tests/phpunit/src/Unit/Helper/Web/RequestHeadersTraitTest.php index 7aa9b642..acb2eeab 100644 --- a/tests/phpunit/src/Unit/Helper/Web/RequestHeadersTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Web/RequestHeadersTraitTest.php @@ -83,7 +83,7 @@ class RequestHeadersTraitTestImplementation { use RequestHeadersTrait; /** - * Read the accumulated headers. + * Reads the accumulated headers. * * @return array * Header values keyed by header name. diff --git a/tests/phpunit/src/Unit/Helper/Web/StringTraitTest.php b/tests/phpunit/src/Unit/Helper/Web/StringTraitTest.php index db98e4c8..af5ccb86 100644 --- a/tests/phpunit/src/Unit/Helper/Web/StringTraitTest.php +++ b/tests/phpunit/src/Unit/Helper/Web/StringTraitTest.php @@ -119,7 +119,7 @@ public function callNormalizeWhitespace(string $value): string { } /** - * Split a comma-separated string. + * Splits a comma-separated string. * * @return array * The trimmed values. diff --git a/tests/phpunit/src/Unit/Steps/Drupal/WatchdogTraitTest.php b/tests/phpunit/src/Unit/Steps/Drupal/WatchdogTraitTest.php index ef81a6e7..ad4e74c7 100644 --- a/tests/phpunit/src/Unit/Steps/Drupal/WatchdogTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Drupal/WatchdogTraitTest.php @@ -60,7 +60,7 @@ public function testOptedInWithoutDblogFailsAtTheStart(): void { /** * Tests that an opted-out scenario calls nothing on any backend. * - * Whether dblog is enabled is never asked, so an opted-out scenario runs + * Whether dblog is enabled is never queried, so an opted-out scenario runs * the same with or without it. * * @param array $steps diff --git a/tests/phpunit/src/Unit/Steps/Web/AccessibilityTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/AccessibilityTraitTest.php index 4b99245f..499cd417 100644 --- a/tests/phpunit/src/Unit/Steps/Web/AccessibilityTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/AccessibilityTraitTest.php @@ -201,8 +201,8 @@ public static function dataProviderFetchJsFromHttpLocation(): array { public function testGetReportDirUsesCapturedBaseDir(): void { AccessibilityTraitTestImplementation::testSetBaseDir('/sentinel/base'); - // Changing directory proves the report directory anchors to the captured - // base rather than the live working directory. + // Changing directory proves the report directory is built from the + // captured base rather than the live working directory. $original = getcwd(); chdir(static::locationsTmp()); @@ -288,9 +288,9 @@ public function testAggregateHtmlSortsRulesBySeverityThenElementCount(): void { $button_name = strpos($html, 'button-name'); $color_contrast = strpos($html, 'color-contrast'); - // Both critical rules precede the serious one, and within the critical - // group the rule with more affected elements (image-alt: 2) precedes the - // one with fewer (button-name: 1). + // Both critical rules precede the serious one. Within the critical group, + // the rule with more affected elements (image-alt: 2) precedes the one + // with fewer (button-name: 1). $this->assertNotFalse($image_alt); $this->assertNotFalse($button_name); $this->assertNotFalse($color_contrast); @@ -365,7 +365,6 @@ public function testAggregateFilenameFormat(): void { $name = AccessibilityTraitTestImplementation::testAggregateFilename(1750000000); $this->assertMatchesRegularExpression('/^accessibility_report_\d{8}_\d{6}\.html$/', $name); - // A different timestamp yields a different filename. $this->assertNotSame($name, AccessibilityTraitTestImplementation::testAggregateFilename(1750086400)); } @@ -488,13 +487,11 @@ public function testRenderJunitFailuresByThreshold(?string $threshold, int $expe $doc = simplexml_load_string($xml); $this->assertInstanceOf(\SimpleXMLElement::class, $doc, 'The rendered report is well-formed XML.'); - // Only violations meeting the effective threshold become cases. $this->assertCount($expected_failures, $doc->xpath('//failure') ?: []); // Every emitted testcase is counted, so tests stays 5 (3 violations + 2 // passes) whether a violation is a failure or advisory. $this->assertCount(5, $doc->xpath('//testcase') ?: []); $this->assertSame('5', (string) $doc['tests']); - // The file-level and suite-level failure counts agree. $this->assertSame((string) $expected_failures, (string) $doc['failures']); $this->assertSame((string) $expected_failures, (string) $doc->testsuite['failures']); } @@ -515,7 +512,6 @@ public function testRenderJunitWarningKeepsAdvisoryViolationsVisibleWithoutFaili $doc = simplexml_load_string($xml); $this->assertInstanceOf(\SimpleXMLElement::class, $doc); - // Advisory mode serialises no violation as a failure. $this->assertCount(0, $doc->xpath('//failure') ?: []); $this->assertStringNotContainsString('. @@ -543,8 +539,8 @@ public function testRenderJunitCountsEveryEmittedTestcase(): void { $doc = simplexml_load_string($xml); $this->assertInstanceOf(\SimpleXMLElement::class, $doc); - // One per affected node (2), plus three passing testcases: tests - // and failures count actual emitted elements, not violations. + // 1 per affected node (2), plus 3 passing testcases: tests and + // failures count actual emitted elements, not violations. $this->assertCount(2, $doc->xpath('//failure') ?: []); $this->assertCount(5, $doc->xpath('//testcase') ?: []); $this->assertSame('5', (string) $doc['tests']); @@ -564,7 +560,7 @@ protected static function renderSample(array $aggregate, string $generated): str } /** - * Builds a representative accumulator with two scenarios, a shared URL, a blank tab, and mixed-impact findings. + * Builds a representative accumulator with 2 scenarios, a shared URL, a blank tab, and mixed-impact findings. * * @return array> * Sample aggregate data in the shape produced by accessibilityAggregateCapture(). @@ -720,11 +716,11 @@ public static function dataProviderSetupScenarioResolvesTags(): array { } /** - * Builds a scenario result with one violation per impact plus two passes. + * Builds a scenario result with 1 violation per impact plus 2 passes. * * @return array}> - * A single-page result: critical, serious and moderate violations (one - * affected node each) alongside two passing rules. + * A single-page result: critical, serious and moderate violations (1 + * affected node each) alongside 2 passing rules. */ protected static function createJunitResults(): array { return [ diff --git a/tests/phpunit/src/Unit/Steps/Web/DateTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/DateTraitTest.php index fa344f74..d156ac5f 100644 --- a/tests/phpunit/src/Unit/Steps/Web/DateTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/DateTraitTest.php @@ -103,9 +103,6 @@ public function testInvalidRelativeDateFormatThrowsException(): void { $this->testObject::dateRelativeProcessValue('[relative:-1 day# ]'); } - /** - * Tests that a skipped scenario passes a token through untouched. - */ public function testSkippedScenarioLeavesTokensUntouched(): void { $this->testObject->dateBeforeScenario($this->createBeforeScenarioScope(['behat-steps-skip:DateTrait'])); @@ -115,9 +112,6 @@ public function testSkippedScenarioLeavesTokensUntouched(): void { $this->assertSame([['created'], ['[relative:-1 day#Y-m-d]']], $this->testObject->dateRelativeTransformTable($table)->getRows()); } - /** - * Tests that an unskipped scenario resolves tokens. - */ public function testUnskippedScenarioResolvesTokens(): void { $this->testObject->dateBeforeScenario($this->createBeforeScenarioScope()); diff --git a/tests/phpunit/src/Unit/Steps/Web/DiagnosticsTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/DiagnosticsTraitTest.php index 29f10b90..4f4f864b 100644 --- a/tests/phpunit/src/Unit/Steps/Web/DiagnosticsTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/DiagnosticsTraitTest.php @@ -56,7 +56,6 @@ public function testDisabledFieldIsOmitted(string $toggle, string $absent_label) $block = $this->testObject->buildBlock(); $this->assertStringNotContainsString($absent_label, $block); - // The block is still produced from the remaining fields. $this->assertStringContainsString('--- Failure diagnostics ---', $block); } diff --git a/tests/phpunit/src/Unit/Steps/Web/FieldTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/FieldTraitTest.php index 3d25c800..a3be28e7 100644 --- a/tests/phpunit/src/Unit/Steps/Web/FieldTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/FieldTraitTest.php @@ -63,7 +63,7 @@ protected function setUp(): void { } public function testFillMultiValueRequiresJavascriptDriver(): void { - // No adapter speaks for a bare driver mock, so the step resolves no + // No adapter supports a bare driver mock, so the step resolves no // JavaScript capability. $this->expectException(UnsupportedDriverActionException::class); $this->expectExceptionMessage(sprintf('No browser capability "%s" is available', JavascriptCapabilityInterface::class)); @@ -74,8 +74,8 @@ public function testFillMultiValueRequiresJavascriptDriver(): void { public function testFillMultiValueThrowsWhenInputRowIsMissing(): void { $this->testObject->getBrowserResolver()->registerAdapter(AnyDriverAdapter::class); - // Zero existing inputs count as one row, so no "Add another item" click - // is attempted and the first value has no input to fill. + // 0 existing inputs count as 1 row, so no "Add another item" click is + // attempted. The first value then has no input to fill. $wrapper = $this->createMock(NodeElement::class); $wrapper->method('findAll')->willReturn([]); $this->page->method('find')->willReturn($wrapper); diff --git a/tests/phpunit/src/Unit/Steps/Web/FileDownloadTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/FileDownloadTraitTest.php index 469e406c..20ed4bd3 100644 --- a/tests/phpunit/src/Unit/Steps/Web/FileDownloadTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/FileDownloadTraitTest.php @@ -215,7 +215,7 @@ class FileDownloadTraitTestImplementation extends WebRawContext { public ?MockResponse $response = NULL; /** - * The options the trait asked the detached browser for. + * The options the trait passed to httpDetachedClient(). * * @var array */ diff --git a/tests/phpunit/src/Unit/Steps/Web/MappingTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/MappingTraitTest.php index c8c63931..261ee865 100644 --- a/tests/phpunit/src/Unit/Steps/Web/MappingTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/MappingTraitTest.php @@ -105,9 +105,6 @@ public function testDuplicateKeyAcrossGroupsFailsTheStep(): void { $this->testObject->mappingTransformValue('{{ Home }}'); } - /** - * Tests that a skipped scenario passes a token through untouched. - */ public function testSkippedScenarioLeavesTokensUntouched(): void { $this->testObject->mappingBeforeScenario($this->createBeforeScenarioScope(['behat-steps-skip:MappingTrait'])); @@ -117,9 +114,6 @@ public function testSkippedScenarioLeavesTokensUntouched(): void { $this->assertSame([['path'], ['{{ User Login }}']], $this->testObject->mappingTransformTable($table)->getRows()); } - /** - * Tests that an unskipped scenario resolves tokens. - */ public function testUnskippedScenarioResolvesTokens(): void { $this->testObject->mappingBeforeScenario($this->createBeforeScenarioScope()); diff --git a/tests/phpunit/src/Unit/Steps/Web/MetatagTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/MetatagTraitTest.php index a0af4e40..6aba475e 100644 --- a/tests/phpunit/src/Unit/Steps/Web/MetatagTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/MetatagTraitTest.php @@ -62,7 +62,7 @@ class MetatagTraitTestImplementation extends WebRawContext { } /** - * The options the trait asked the detached browser for. + * The options the trait passed to httpDetachedClient(). * * @var array */ diff --git a/tests/phpunit/src/Unit/Steps/Web/ModalTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/ModalTraitTest.php index 013a2025..7cdb1d03 100644 --- a/tests/phpunit/src/Unit/Steps/Web/ModalTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/ModalTraitTest.php @@ -54,7 +54,7 @@ protected function setUp(): void { public function testCloseThrowsWhenVisibleModalHasNoCloseButton(): void { $modal = $this->createVisibleModal(); - // One find() per close selector. + // 1 find() per close selector. $modal->expects($this->exactly(3))->method('find')->willReturn(NULL); $this->expectException(ElementNotFoundException::class); @@ -66,7 +66,7 @@ public function testCloseThrowsWhenVisibleModalHasNoCloseButton(): void { #[DataProvider('dataProviderAssertContainsThrowsWhenModalHasNoContentElement')] public function testAssertContainsThrowsWhenModalHasNoContentElement(string $method): void { $modal = $this->createVisibleModal(); - // One find() per content selector. + // 1 find() per content selector. $modal->expects($this->exactly(3))->method('find')->willReturn(NULL); $this->expectException(ElementNotFoundException::class); @@ -83,7 +83,7 @@ public static function dataProviderAssertContainsThrowsWhenModalHasNoContentElem } /** - * Put a visible modal on the page. + * Puts a visible modal on the page. * * A visible modal passes modalGetVisible(), so the failure under test is * the lookup inside the modal. diff --git a/tests/phpunit/src/Unit/Steps/Web/RandomTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/RandomTraitTest.php index 72def7ef..ab2dc089 100644 --- a/tests/phpunit/src/Unit/Steps/Web/RandomTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/RandomTraitTest.php @@ -48,7 +48,7 @@ public function testUnskippedScenarioResolvesTokens(): void { $this->assertNotSame('[?title]', $resolved); $this->assertSame(10, strlen($resolved)); - // One value per token for the whole scenario, so the table cell holds the + // 1 value per token for the whole scenario, so the table cell holds the // same string the scalar argument resolved to. $table = new TableNode([['title'], ['[?title]']]); $this->assertSame([['title'], [$resolved]], $this->testObject->randomTransformTable($table)->getRows()); diff --git a/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php b/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php index 3c7f8ce6..e1fe2539 100644 --- a/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php +++ b/tests/phpunit/src/Unit/Steps/Web/XmlTraitTest.php @@ -48,7 +48,7 @@ class XmlTraitTestImplementation extends RawMinkContext { use XmlTrait; /** - * Install a document as the one loaded for the test content. + * Installs a document as the one loaded for the test content. * * Content and document set together make xmlEnsureDocument() keep the * document instead of loading the content. diff --git a/tests/phpunit/src/UnitTestCase.php b/tests/phpunit/src/UnitTestCase.php index a8ccd69c..4fb1b996 100644 --- a/tests/phpunit/src/UnitTestCase.php +++ b/tests/phpunit/src/UnitTestCase.php @@ -35,11 +35,12 @@ abstract class UnitTestCase extends UpstreamUnitTestCase { /** * Indicates whether a path under `src/` holds a trait a context composes. * - * The conventions the discovery-driven tests hold describe the traits this - * library names itself and flattens into a consuming context: the step - * vocabulary under `Steps/` and the helpers under `Helper/`. The traits - * under `Behat/` carry the names the framework interfaces dictate, and the - * backend layer is library code with its own shapes. + * The discovery-driven tests hold conventions for the traits this library + * names itself and flattens into a consuming context. Those are the step + * vocabulary under `Steps/` and the helpers under `Helper/`. + * + * The traits under `Behat/` carry the names the framework interfaces + * dictate, and the backend layer is library code with its own shapes. * * @param string $relative_path * A path relative to `src/`. From f85655e6f014a91ba6398bb012d6f82dbdc0b703 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Sat, 3 Oct 2026 14:18:13 +1000 Subject: [PATCH 15/38] Corrected 77 comments whose claims disagreed with the code, regenerating 'STEPS.md', 'HELPERS.md' and the 'README.md' index. --- HELPERS.md | 6 +- README.md | 8 +- STEPS.md | 114 +++++++++--------- behat.dist.php | 5 +- docs.php | 7 +- rector.php | 8 +- scripts/check-coverage.php | 5 +- src/Backend/Alias/CreationAliasInterface.php | 6 +- .../Capability/ConfigCapabilityInterface.php | 7 +- .../Capability/DrushCapabilityInterface.php | 2 +- .../Core/Field/Parser/EntityFieldParser.php | 6 +- src/Backend/DrupalBackend.php | 3 +- src/Backend/DrushBackend.php | 2 +- src/Backend/Entity/EntityStub.php | 7 +- src/Backend/Entity/EntityStubInterface.php | 7 +- .../Initializer/BackendAwareInitializer.php | 4 +- src/Behat/Context/WebRawContext.php | 6 +- .../Hook/Attribute/DrupalHookInterface.php | 4 +- src/Behat/Mink/Adapter/BrowserKitAdapter.php | 7 +- src/Helper/Drupal/FixtureFileTrait.php | 6 +- src/Helper/Web/RequestHeadersTrait.php | 5 +- src/Helper/Web/TableTransposeTrait.php | 2 +- src/Steps/Drupal/CacheTrait.php | 3 - src/Steps/Drupal/ConfigOverrideTrait.php | 24 ++-- src/Steps/Drupal/ConfigTrait.php | 20 ++- src/Steps/Drupal/DraggableviewsTrait.php | 2 +- src/Steps/Drupal/MenuTrait.php | 2 +- src/Steps/Drupal/SearchApiTrait.php | 5 +- src/Steps/Drupal/StateTrait.php | 10 +- src/Steps/Web/AccessibilityTrait.php | 9 +- src/Steps/Web/CookieTrait.php | 4 +- src/Steps/Web/DateTrait.php | 8 +- src/Steps/Web/ElementTrait.php | 10 +- src/Steps/Web/FieldTrait.php | 16 +-- src/Steps/Web/FileDownloadTrait.php | 2 +- src/Steps/Web/JavascriptTrait.php | 8 +- src/Steps/Web/KeyboardTrait.php | 8 +- src/Steps/Web/LinkTrait.php | 4 +- src/Steps/Web/MessageTrait.php | 8 +- src/Steps/Web/RandomTrait.php | 2 +- src/Steps/Web/ResponseTrait.php | 2 +- src/Steps/Web/TableTrait.php | 8 +- src/Steps/Web/WaitTrait.php | 6 +- src/Steps/Web/XmlTrait.php | 4 +- tests/behat/bootstrap/FeatureContextTrait.php | 2 +- tests/phpunit/src/DocsTest.php | 2 +- .../Core/CoreConfigMethodsKernelTest.php | 7 +- .../Core/CoreUserMethodsKernelTest.php | 7 +- .../Core/Field/AddressHandlerKernelTest.php | 3 +- .../EntityReferenceHandlerKernelTest.php | 5 +- .../Unit/Backend/Core/CorePermissionsTest.php | 2 +- .../Field/AbstractHandlerNormalizeTest.php | 2 +- .../Core/Field/DatetimeHandlerTest.php | 7 +- .../Core/Field/FileBackedHandlerTestBase.php | 2 +- .../Backend/Core/Field/FileHandlerTest.php | 2 +- .../src/Unit/Backend/DrupalBackendTest.php | 8 +- .../Unit/Backend/DrushBackendMethodsTest.php | 8 +- .../Behat/Fixtures/ConfigurableContext.php | 2 +- .../Unit/Behat/Manager/AuthenticatorTest.php | 5 +- .../Behat/Manager/BackendRegistryTest.php | 2 +- .../Helper/Drupal/FixtureFileTraitTest.php | 2 +- .../src/Unit/Steps/Web/DateTraitTest.php | 2 +- 62 files changed, 242 insertions(+), 220 deletions(-) diff --git a/HELPERS.md b/HELPERS.md index 8854c680..3d68d6a0 100644 --- a/HELPERS.md +++ b/HELPERS.md @@ -48,7 +48,7 @@ | [Drupal\EntityTrait](#drupalentitytrait) | 6 | Create entities of a type that has no dedicated trait. | | [Drupal\FileTrait](#drupalfiletrait) | 3 | Manage Drupal file entities with upload and storage operations. | | [Drupal\MediaTrait](#drupalmediatrait) | 4 | Manage Drupal media entities with type-specific field handling. | -| [Drupal\MenuTrait](#drupalmenutrait) | 2 | Manage Drupal menu systems and menu link rendering. | +| [Drupal\MenuTrait](#drupalmenutrait) | 2 | Manage Drupal menus and menu links. | | [Drupal\ModuleTrait](#drupalmoduletrait) | 4 | Enable and disable Drupal modules with automatic state restoration. | | [Drupal\ParagraphsTrait](#drupalparagraphstrait) | 2 | Manage Drupal paragraphs entities with structured field data. | | [Drupal\QueueTrait](#drupalqueuetrait) | 2 | Manage and assert Drupal queue state. | @@ -918,7 +918,7 @@ Generates an integer in '[min, max]' inclusive public function randomGenerateMachineName(int $length): string
-Generates a Drupal-shaped machine name (lowercase + underscores) +Generates a lowercase alphanumeric machine name starting with a letter

@@ -1669,7 +1669,7 @@ Visit the action page of the media with a specified name [Source](src/Steps/Drupal/MenuTrait.php), [Steps](STEPS.md#drupalmenutrait) -> Manage Drupal menu systems and menu link rendering. +> Manage Drupal menus and menu links.
public function menuFindByLabel(string $label): ?MenuInterface diff --git a/README.md b/README.md index 671b7aba..a44d75b4 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ See [MIGRATION.md](MIGRATION.md) for migration guides. | [IframeTrait](STEPS.md#iframetrait) | Switch between iframes and the root document. | | [JavascriptTrait](STEPS.md#javascripttrait) | Automatically detect JavaScript errors during test execution. | | [JsonTrait](STEPS.md#jsontrait) | Assert JSON responses with path and schema checks. | -| [KeyboardTrait](STEPS.md#keyboardtrait) | Simulate keyboard interactions in Drupal browser testing. | +| [KeyboardTrait](STEPS.md#keyboardtrait) | Simulate keyboard interactions in the browser. | | [LinkTrait](STEPS.md#linktrait) | Verify link elements with attribute and content assertions. | | [MappingTrait](STEPS.md#mappingtrait) | Replace `{{ Key }}` tokens in step arguments and table cells. | | [MessageTrait](STEPS.md#messagetrait) | Assert status, error, warning and success messages rendered on the page. | @@ -80,7 +80,7 @@ See [MIGRATION.md](MIGRATION.md) for migration guides. | [PathTrait](STEPS.md#pathtrait) | Navigate and verify paths with URL validation. | | [RandomTrait](STEPS.md#randomtrait) | Replace random-value tokens in step arguments and table cells. | | [RegionTrait](STEPS.md#regiontrait) | Interact with and assert against named page regions. | -| [ResponseTrait](STEPS.md#responsetrait) | Verify HTTP responses with status code and header checks. | +| [ResponseTrait](STEPS.md#responsetrait) | Verify HTTP response headers. | | [ResponsiveTrait](STEPS.md#responsivetrait) | Test responsive layouts with viewport control. | | [RestTrait](STEPS.md#resttrait) | Lightweight REST API testing with no Drupal dependencies. | | [TableTrait](STEPS.md#tabletrait) | Interact with HTML table elements and assert their content. | @@ -107,12 +107,12 @@ See [MIGRATION.md](MIGRATION.md) for migration guides. | [Drupal\FileTrait](STEPS.md#drupalfiletrait) | Manage Drupal file entities with upload and storage operations. | | [Drupal\LanguageTrait](STEPS.md#drupallanguagetrait) | Create the languages a scenario needs. | | [Drupal\MediaTrait](STEPS.md#drupalmediatrait) | Manage Drupal media entities with type-specific field handling. | -| [Drupal\MenuTrait](STEPS.md#drupalmenutrait) | Manage Drupal menu systems and menu link rendering. | +| [Drupal\MenuTrait](STEPS.md#drupalmenutrait) | Manage Drupal menus and menu links. | | [Drupal\ModuleTrait](STEPS.md#drupalmoduletrait) | Enable and disable Drupal modules with automatic state restoration. | | [Drupal\ParagraphsTrait](STEPS.md#drupalparagraphstrait) | Manage Drupal paragraphs entities with structured field data. | | [Drupal\QueueTrait](STEPS.md#drupalqueuetrait) | Manage and assert Drupal queue state. | | [Drupal\RedirectTrait](STEPS.md#drupalredirecttrait) | Manage Drupal redirect entities provided by the contrib `redirect` module. | -| [Drupal\SearchApiTrait](STEPS.md#drupalsearchapitrait) | Assert Drupal Search API with index and query operations. | +| [Drupal\SearchApiTrait](STEPS.md#drupalsearchapitrait) | Run Drupal Search API indexing and cron hooks. | | [Drupal\StateTrait](STEPS.md#drupalstatetrait) | Manage and assert Drupal State API values with automatic revert. | | [Drupal\TaxonomyTrait](STEPS.md#drupaltaxonomytrait) | Manage Drupal taxonomy terms with vocabulary organization. | | [Drupal\TestmodeTrait](STEPS.md#drupaltestmodetrait) | Configure Drupal Testmode module for controlled testing scenarios. | diff --git a/STEPS.md b/STEPS.md index 804ceb31..3967434e 100644 --- a/STEPS.md +++ b/STEPS.md @@ -17,7 +17,7 @@ | [IframeTrait](#iframetrait) | Switch between iframes and the root document. | | [JavascriptTrait](#javascripttrait) | Automatically detect JavaScript errors during test execution. | | [JsonTrait](#jsontrait) | Assert JSON responses with path and schema checks. | -| [KeyboardTrait](#keyboardtrait) | Simulate keyboard interactions in Drupal browser testing. | +| [KeyboardTrait](#keyboardtrait) | Simulate keyboard interactions in the browser. | | [LinkTrait](#linktrait) | Verify link elements with attribute and content assertions. | | [MappingTrait](#mappingtrait) | Replace `{{ Key }}` tokens in step arguments and table cells. | | [MessageTrait](#messagetrait) | Assert status, error, warning and success messages rendered on the page. | @@ -26,7 +26,7 @@ | [PathTrait](#pathtrait) | Navigate and verify paths with URL validation. | | [RandomTrait](#randomtrait) | Replace random-value tokens in step arguments and table cells. | | [RegionTrait](#regiontrait) | Interact with and assert against named page regions. | -| [ResponseTrait](#responsetrait) | Verify HTTP responses with status code and header checks. | +| [ResponseTrait](#responsetrait) | Verify HTTP response headers. | | [ResponsiveTrait](#responsivetrait) | Test responsive layouts with viewport control. | | [RestTrait](#resttrait) | Lightweight REST API testing with no Drupal dependencies. | | [TableTrait](#tabletrait) | Interact with HTML table elements and assert their content. | @@ -53,12 +53,12 @@ | [Drupal\FileTrait](#drupalfiletrait) | Manage Drupal file entities with upload and storage operations. | | [Drupal\LanguageTrait](#drupallanguagetrait) | Create the languages a scenario needs. | | [Drupal\MediaTrait](#drupalmediatrait) | Manage Drupal media entities with type-specific field handling. | -| [Drupal\MenuTrait](#drupalmenutrait) | Manage Drupal menu systems and menu link rendering. | +| [Drupal\MenuTrait](#drupalmenutrait) | Manage Drupal menus and menu links. | | [Drupal\ModuleTrait](#drupalmoduletrait) | Enable and disable Drupal modules with automatic state restoration. | | [Drupal\ParagraphsTrait](#drupalparagraphstrait) | Manage Drupal paragraphs entities with structured field data. | | [Drupal\QueueTrait](#drupalqueuetrait) | Manage and assert Drupal queue state. | | [Drupal\RedirectTrait](#drupalredirecttrait) | Manage Drupal redirect entities provided by the contrib `redirect` module. | -| [Drupal\SearchApiTrait](#drupalsearchapitrait) | Assert Drupal Search API with index and query operations. | +| [Drupal\SearchApiTrait](#drupalsearchapitrait) | Run Drupal Search API indexing and cron hooks. | | [Drupal\StateTrait](#drupalstatetrait) | Manage and assert Drupal State API values with automatic revert. | | [Drupal\TaxonomyTrait](#drupaltaxonomytrait) | Manage Drupal taxonomy terms with vocabulary organization. | | [Drupal\TestmodeTrait](#drupaltestmodetrait) | Configure Drupal Testmode module for controlled testing scenarios. | @@ -349,8 +349,8 @@ Then the command should complete in more than 1 second > Verify and inspect browser cookies. > - Assert cookie existence and values with exact or partial matching. -> - Support both WebDriver and BrowserKit browser drivers for test -> compatibility. +> - Read cookies through whichever browser driver provides the cookie +> capability.
@@ -539,7 +539,7 @@ Then a cookie with a name containing "user" and a value containing "guest" shoul > > Examples: > - `[relative:-1 day]` converted to `1893456000` -> - `[relative:-1 day#Y-m-d]` converted to `2017-11-5` +> - `[relative:-1 day#Y-m-d]` converted to `2017-11-05` > > `dateRelativeProcessValue()` is public API. It and its helpers are static, > so a token resolves without a context instance. @@ -778,7 +778,7 @@ When I trigger the JS event "click" on the element "#submit-button" @When I scroll to the element :selector
-Scroll to an element with ID +Scroll to the element matching a CSS selector

```gherkin @@ -934,7 +934,7 @@ Then the element "#main-content" with the attribute "class" and a value containi @Then the element :selector with the attribute :attribute and the value :value should not exist
-Assert an element with selector and attribute with a value exists +Assert an element with selector and attribute with a value does not exist

```gherkin @@ -1725,7 +1725,7 @@ Then the radio button "edit-field-choice-option-b" should not be selected > Test file download functionality with content verification. > - Download files through links and URLs with session cookie handling. -> - Verify file names, content, and extracted archives. +> - Verify file names, content, and zip archive entries. > - Set up download directories and handle file cleanup. > > Skip processing with tag: `@behat-steps-skip:FileDownloadTrait`. @@ -1911,10 +1911,12 @@ When I switch to the root document [Source](src/Steps/Web/JavascriptTrait.php), [Example](tests/behat/features/javascript.feature) > Automatically detect JavaScript errors during test execution. -> - Collects JavaScript errors from `window.onerror` and `console.error`. +> - Collects JavaScript errors from `window.onerror`, `unhandledrejection` +> and `console.error`. > - Automatically asserts no errors at end of scenarios with `@javascript` tag. -> - Errors collected only when URL changes (navigation occurs). -> - Use `@js-errors` tag to bypass error checking when errors are expected. +> - Collects errors after every step and re-injects the collector when the +> URL changes. +> - The `@js-errors` tag bypasses error checking when errors are expected. > > Skip processing with tags: `@behat-steps-skip:JavascriptTrait` >

@@ -2244,10 +2246,10 @@ Then the response should match the JSON schema in the file "json_schema.json" [Source](src/Steps/Web/KeyboardTrait.php), [Example](tests/behat/features/keyboard.feature) -> Simulate keyboard interactions in Drupal browser testing. -> - Trigger key press events including special keys and key combinations. -> - Assert keyboard navigation and shortcut functionality. -> - Support for targeted key presses on specific page elements. +> Simulate keyboard interactions in the browser. +> - Trigger key press events, including named special keys. +> - Press a string of characters 1 key at a time. +> - Target a key press at a page element or at the focused element.
@@ -2313,8 +2315,8 @@ When I press the keys "abc" on the element "#edit-title" [Source](src/Steps/Web/LinkTrait.php), [Example](tests/behat/features/link.feature) > Verify link elements with attribute and content assertions. -> - Find links by title, URL, text content, and class attributes. -> - Test link existence, visibility, and destination accuracy. +> - Find links by title, or by text and href, optionally within an element. +> - Assert link existence and href match. > - Assert absolute and relative link paths. @@ -2484,10 +2486,10 @@ Then the link "Return to site content" should not be an absolute link > - Match a single message by substring, per message type. > - Match a table of messages in one step. > -> Each message type resolves to a CSS selector configured under the -> `selectors: messages:` map in the extension configuration, keyed `default`, -> `error`, `success` and `warning`. A message matches when the text of any -> element found by that selector contains the expected string. +> Each message type resolves to a CSS selector from the `message.selectors` +> option, keyed `default`, `error`, `success` and `warning`. A message +> matches when the text of any element found by that selector contains the +> expected string. ### Options @@ -3533,7 +3535,7 @@ Then the element "span" with the text "New" in the region "content" should have [Source](src/Steps/Web/ResponseTrait.php), [Example](tests/behat/features/response.feature) -> Verify HTTP responses with status code and header checks. +> Verify HTTP response headers. > - Assert HTTP header presence and values. @@ -3816,7 +3818,7 @@ Then the REST response should contain "success" > Interact with HTML table elements and assert their content. > - Assert table row and column counts. -> - Assert table column headers in thead. +> - Assert table column headers. > - Assert table empty and non-empty states. > - Assert table sort order by column. > - Assert text values present in a specific table row. @@ -3863,7 +3865,7 @@ When I press the button "Remove" in the row "Article title" @Then the table :selector should have :count row(s)
-Assert that a table has the expected number of rows in its tbody +Assert that a table has the expected number of body rows

```gherkin @@ -3908,7 +3910,7 @@ Then the table ".mytable" should contain the following columns: @Then the table :selector should be empty
-Assert that a table is empty (has no rows in tbody) +Assert that a table is empty (has no body rows)

```gherkin @@ -3922,7 +3924,7 @@ Then the table ".mytable" should be empty @Then the table :selector should not be empty
-Assert that a table is not empty (has rows in tbody) +Assert that a table is not empty (has body rows)

```gherkin @@ -4045,9 +4047,9 @@ Then the link "Delete" should not exist in the row "Article title" > - Wait for jQuery and Drupal AJAX activity to settle, on demand or around > every step that navigates or submits. >

-> Mink's own AJAX wait watches `jQuery.active` alone, while Drupal renders many -> updates through `Drupal.ajax`. An assertion following a click can read the -> page before the update applies, so the wait here watches both. +> A wait on `jQuery.active` alone misses the updates Drupal renders through +> `Drupal.ajax`, so an assertion following a click can read the page before +> the update applies. The wait here watches both. >

> Skip the automatic waits with tag: `@behat-steps-skip:WaitTrait`. @@ -4882,16 +4884,20 @@ When I run cron > runtime. They cannot be disabled from the Behat process because tests run > in a separate process from the system under test (SUT). >

-> This trait signals the SUT - through a request header, a `$_SERVER` entry -> and an environment variable - that specific config objects should be read -> from their original (unoverridden) values. The SUT is responsible for -> reading that signal and calling `ImmutableConfig::getOriginal()` instead of -> `ImmutableConfig::get()` for the listed config names. +> This trait signals the SUT that specific config objects should be read +> from their original (unoverridden) values. The signal is a request header, +> a `$_SERVER` entry and an environment variable. +>

+> The SUT is responsible for reading that signal and calling +> `ImmutableConfig::getOriginal()` instead of `ImmutableConfig::get()` for +> the listed config names. >

> Activated by adding `@disable-config-override:CONFIG_NAME` tags to a > feature or scenario. Multiple tags are combined into a comma-separated -> list. Runs on every step because some steps reset headers set earlier in -> the scenario. +> list. +>

+> The signal is applied before every step because some steps reset headers +> set earlier in the scenario. >

> Limitations: > - The request header reaches the SUT only on a browser driver providing @@ -4913,9 +4919,9 @@ When I run cron > ``` >

> The signal is also written to the request-header bag, so a trait that -> issues its own HTTP requests - `RestTrait` - carries it too. The bag is -> per context, so that reaches `RestTrait` only where one context composes -> both; the shipped `WebContext` and `DrupalContext` are separate objects. +> issues its own HTTP requests - `RestTrait` - carries it too. The bag is a +> property of the context object, so the signal reaches `RestTrait` when the +> same context composes both traits, as the shipped `DrupalContext` does. >

> Example: > ``` @@ -4944,13 +4950,12 @@ When I run cron > object's key holds, or contains, an expected value. Nested keys are > addressable with dotted notation (for example `page.front`). >

-> Two families of assertions read the value differently: -> - The default steps read the STORED value via editable configuration, -> ignoring `settings.php` overrides. This is symmetric with the set steps -> and is what most setup-and-assert scenarios need. -> - The `effective` steps read the value through the config factory with -> module and `settings.php` overrides applied - the value the running site -> actually uses. +> 2 families of assertions read the value differently: +> - The default steps read the stored value, with module and `settings.php` +> overrides left unapplied. This is symmetric with the set steps and is +> what most setup-and-assert scenarios need. +> - The `effective` steps read the value with module and `settings.php` +> overrides applied: the value the running site uses. >

> Values are compared by their stringified form, so `true`, `42` and JSON > arrays written in a step match their typed configuration counterparts. The @@ -4958,8 +4963,8 @@ When I run cron > array values, searched recursively. >

> Configuration objects touched by the set steps are snapshotted on first -> write and restored after the scenario: an existing object is reset to its -> original data and an object that did not exist is deleted. Skip the revert +> write and restored after the scenario. An existing object is reset to its +> original data, and an object that did not exist is deleted. Skip the revert > with `@behat-steps-skip:ConfigTrait`. >

> ``` @@ -5485,7 +5490,7 @@ Then the "page" content with the title "Test page" should not be published @When I save the draggable views items of the view :view_id and the display :view_display_id for the :content_type content in the following order:
-Save order of the Draggable Order items +Save the order of the Draggable Views items

```gherkin @@ -6507,7 +6512,7 @@ Then the "image" media with the name "Test media image" should not exist [Source](src/Steps/Drupal/MenuTrait.php), [Example](tests/behat/features/drupal_menu.feature) -> Manage Drupal menu systems and menu link rendering. +> Manage Drupal menus and menu links. > - Create and remove menus by label. > - Create and remove menu links, including parent-child hierarchies. > - Created menus and menu links are automatically removed at the end of the scenario. @@ -6959,9 +6964,10 @@ Then the following redirects should not exist: [Source](src/Steps/Drupal/SearchApiTrait.php), [Example](tests/behat/features/drupal_search_api.feature) -> Assert Drupal Search API with index and query operations. -> - Add content to an index +> Run Drupal Search API indexing and cron hooks. +> - Add content to an index. > - Run indexing for a specific number of items. +> - Run the Search API and Search API Solr cron hooks. ### Prerequisites diff --git a/behat.dist.php b/behat.dist.php index 08ea0f4a..c9d1c9df 100644 --- a/behat.dist.php +++ b/behat.dist.php @@ -1,7 +1,10 @@ |string>>|string>> $info - * Array of info items with 'name', 'from', and 'to' keys. + * The extracted trait info from extract_info(). * @param string $base_path * Base path for the repository. * @@ -1535,7 +1536,7 @@ function validate_helpers(array $info): array { * Validate the info. * * @param array|string>>|string>> $info - * Array of info items with 'name', 'from', and 'to' keys. + * The extracted trait info from extract_info(). * * @return array * Array of errors. diff --git a/rector.php b/rector.php index f51dea519..d2ad446f 100644 --- a/rector.php +++ b/rector.php @@ -4,12 +4,10 @@ * @file * Rector configuration. * - * Rector automatically refactors PHP code to: - * - Upgrade deprecated Drupal APIs - * - Modernize PHP syntax to leverage new language features - * - Improve code quality and maintainability + * Rector rewrites the sources to PHP 8.3 syntax and to Behat's step, hook and + * transformation attributes. It also applies the code quality, coding style, + * dead code, naming, privatization and type declaration sets. * - * @see https://github.com/palantirnet/drupal-rector * @see https://getrector.com/documentation * @see https://getrector.com/documentation/set-lists */ diff --git a/scripts/check-coverage.php b/scripts/check-coverage.php index 5eebe4c3..9f52ae24 100644 --- a/scripts/check-coverage.php +++ b/scripts/check-coverage.php @@ -9,8 +9,9 @@ * * Where: * - TraitName: The name of the trait to check (e.g., "ElementTrait") - * - coverage_file_path: Optional path to the cobertura.xml file. - * Defaults to '/app/.logs/coverage/behat_cli/cobertura.xml'. + * - coverage_file_path: Optional path to the cobertura.xml file. Defaults to + * '.logs/coverage/behat_cli/cobertura.xml', read from '/app' when the file + * exists there and from the repository root otherwise. * * Examples: * php check-coverage.php ElementTrait diff --git a/src/Backend/Alias/CreationAliasInterface.php b/src/Backend/Alias/CreationAliasInterface.php index f34212a7..eaa8953d 100644 --- a/src/Backend/Alias/CreationAliasInterface.php +++ b/src/Backend/Alias/CreationAliasInterface.php @@ -34,11 +34,11 @@ public function getEntityType(): string; /** * Returns a human-readable description of what this alias does. * - * Should describe the input shape, the resolution behaviour, and the - * resulting effect on the created entity in a single sentence. + * Covers the input shape, the resolution behaviour and the resulting effect + * on the created entity in a few short sentences. * * @return string - * A single-sentence description of the alias's behaviour. + * A short prose description of the alias's behaviour. */ public function getDescription(): string; diff --git a/src/Backend/Capability/ConfigCapabilityInterface.php b/src/Backend/Capability/ConfigCapabilityInterface.php index 025fa45b..17d02d57 100644 --- a/src/Backend/Capability/ConfigCapabilityInterface.php +++ b/src/Backend/Capability/ConfigCapabilityInterface.php @@ -23,7 +23,10 @@ interface ConfigCapabilityInterface { public function configGet(string $name, string $key = ''): mixed; /** - * Returns the original (on-disk) configuration value. + * Returns a configuration value with overrides not applied. + * + * Module and 'settings.php' overrides are left out, so the result is the + * stored value a write replaces. * * @param string $name * The configuration object name. @@ -31,7 +34,7 @@ public function configGet(string $name, string $key = ''): mixed; * The key within the configuration object. Empty for the whole object. * * @return mixed - * The original configuration value, or NULL if not set. + * The stored configuration value, or NULL if not set. */ public function configGetOriginal(string $name, string $key = ''): mixed; diff --git a/src/Backend/Capability/DrushCapabilityInterface.php b/src/Backend/Capability/DrushCapabilityInterface.php index 45ac02a7..1debbaf5 100644 --- a/src/Backend/Capability/DrushCapabilityInterface.php +++ b/src/Backend/Capability/DrushCapabilityInterface.php @@ -22,7 +22,7 @@ interface DrushCapabilityInterface { * Options to pass to Drush. * * @return string - * The command's stdout, or its stderr when stdout is empty. + * The command's stdout, or its stderr when stdout is empty or '0'. * * @throws \RuntimeException * When the command exits with a non-zero status. diff --git a/src/Backend/Core/Field/Parser/EntityFieldParser.php b/src/Backend/Core/Field/Parser/EntityFieldParser.php index b9ca9b91..3fb8f94f 100644 --- a/src/Backend/Core/Field/Parser/EntityFieldParser.php +++ b/src/Backend/Core/Field/Parser/EntityFieldParser.php @@ -637,9 +637,9 @@ protected function readQuotedString(string $fragment, int &$offset, ?string $err * Reads a '[name:value]' token starting at the current offset. * * Advances $offset past the closing bracket and returns the verbatim - * '[...]' substring (downstream field handlers expand the token). When - * called for cell-level diagnostics, $error_cell and $error_base_offset - * are used to report errors against the original cell. + * '[...]' substring, which becomes the column value unchanged. When called + * for cell-level diagnostics, $error_cell and $error_base_offset are used + * to report errors against the original cell. */ protected function readToken(string $fragment, int &$offset, ?string $error_cell = NULL, int $error_base_offset = 0): string { $error_cell ??= $fragment; diff --git a/src/Backend/DrupalBackend.php b/src/Backend/DrupalBackend.php index db8c58a6..c9de355f 100644 --- a/src/Backend/DrupalBackend.php +++ b/src/Backend/DrupalBackend.php @@ -104,7 +104,8 @@ public function getDrupalVersion(): int { * the detected one downwards, then 'DrevOps\BehatSteps\Backend\Core\Core'. * * @throws \DrevOps\BehatSteps\Backend\Exception\BootstrapException - * Thrown when no Core implementation is found for the detected version. + * When a version-specific Core class exists but does not implement + * 'CoreInterface'. */ public function setCoreFromVersion(): void { $version = $this->getDrupalVersion(); diff --git a/src/Backend/DrushBackend.php b/src/Backend/DrushBackend.php index f682a75e..6747f062 100644 --- a/src/Backend/DrushBackend.php +++ b/src/Backend/DrushBackend.php @@ -574,7 +574,7 @@ public function drushResult(string $command, array $arguments = [], array $optio * Options to pass to Drush. * * @return string - * The command's stdout, or its stderr when stdout is empty. + * The command's stdout, or its stderr when stdout is empty or '0'. * * @throws \RuntimeException * When the command exits with a non-zero status. diff --git a/src/Backend/Entity/EntityStub.php b/src/Backend/Entity/EntityStub.php index f6a8f9b4..5a70eb8a 100644 --- a/src/Backend/Entity/EntityStub.php +++ b/src/Backend/Entity/EntityStub.php @@ -7,9 +7,10 @@ /** * Typed envelope for creating, tracking, and cleaning up a Drupal entity. * - * Mirrors Drupal Core's own 'Entity::create($type, $values)' shape - one - * final class, no subclasses, with the entity type and bundle pinned at - * construction time and a mutable values bag plus a saved-entity slot. + * Like Drupal Core's 'getStorage($type)->create($values)', which creates an + * entity of any type from 1 call, the stub is 1 final class with no + * subclasses. The entity type and bundle are pinned at construction time; + * the values bag and the saved-entity slot are mutable. */ final class EntityStub implements EntityStubInterface { diff --git a/src/Backend/Entity/EntityStubInterface.php b/src/Backend/Entity/EntityStubInterface.php index 7663a502..6c84248b 100644 --- a/src/Backend/Entity/EntityStubInterface.php +++ b/src/Backend/Entity/EntityStubInterface.php @@ -14,11 +14,10 @@ interface EntityStubInterface { /** - * Default bundle key for entity types that do not declare one. + * Bundle key a stub starts with, until 'setBundleKey()' replaces it. * - * Drupal Core's most common bundle key is 'type' (used by 'node', - * 'block_content', 'entity_test', and others), so it is the fallback when - * the caller has not specified a bundle key. + * Drupal Core's most common bundle key is 'type', used by 'node', + * 'block_content', 'entity_test' and others. */ public const DEFAULT_BUNDLE_KEY = 'type'; diff --git a/src/Behat/Context/Initializer/BackendAwareInitializer.php b/src/Behat/Context/Initializer/BackendAwareInitializer.php index db6686d6..3eba1fca 100644 --- a/src/Behat/Context/Initializer/BackendAwareInitializer.php +++ b/src/Behat/Context/Initializer/BackendAwareInitializer.php @@ -76,8 +76,8 @@ public function initializeContext(Context $context): void { $context->setBasicAuthenticator($this->basicAuthenticator); $context->setHttpClientFactory($this->httpClientFactory); - // Set last: it rebuilds the resolver, so the parameters set above are the - // ones the rebuild reads its 'steps' section from. + // Set last: a context that rebuilds its resolver in this call reads the + // 'steps' section from the parameters set above. $context->setOptionResolverFactory($this->optionResolverFactory); } diff --git a/src/Behat/Context/WebRawContext.php b/src/Behat/Context/WebRawContext.php index f500811a..bc04b56e 100644 --- a/src/Behat/Context/WebRawContext.php +++ b/src/Behat/Context/WebRawContext.php @@ -93,7 +93,11 @@ class WebRawContext extends RawMinkContext implements BackendAwareInterface { protected ?TraitOptionResolverFactoryInterface $optionResolverFactory = NULL; /** - * Resolves the options this context's traits declare, NULL until first read. + * Resolves the options this context's traits declare. + * + * Built by the constructor, then NULL between a 'setParameters()' or + * 'setOptionResolverFactory()' call and the next 'getOptionResolver()' + * call, which rebuilds it. */ protected ?TraitOptionResolverInterface $optionResolver = NULL; diff --git a/src/Behat/Hook/Attribute/DrupalHookInterface.php b/src/Behat/Hook/Attribute/DrupalHookInterface.php index 6711fe77..ae0dc6d4 100644 --- a/src/Behat/Hook/Attribute/DrupalHookInterface.php +++ b/src/Behat/Hook/Attribute/DrupalHookInterface.php @@ -5,7 +5,9 @@ namespace DrevOps\BehatSteps\Behat\Hook\Attribute; /** - * Marker interface for the entity creation hook attributes. + * Contract for the entity creation hook attributes. + * + * Each attribute exposes the filter string it was declared with. */ interface DrupalHookInterface { diff --git a/src/Behat/Mink/Adapter/BrowserKitAdapter.php b/src/Behat/Mink/Adapter/BrowserKitAdapter.php index c1990571..28aa22bf 100644 --- a/src/Behat/Mink/Adapter/BrowserKitAdapter.php +++ b/src/Behat/Mink/Adapter/BrowserKitAdapter.php @@ -38,10 +38,9 @@ public function cookieGetAll(): array { $jar = $driver->getClient()->getCookieJar(); // The value list holds 1 entry per name, already resolved for the current - // URL by domain, path and secure flag. The cookie objects supply the - // remaining properties, and 'all()' flattens every domain and path - // together, so several objects can share a name: the one carrying the - // resolved value is the one that belongs to this URL. + // URL by domain, path and secure flag. 'all()' flattens every domain and + // path together, so several objects can share a name: the one carrying + // the resolved value is the one that belongs to this URL. $resolved = $jar->allValues($driver->getCurrentUrl(), TRUE); $cookies = []; diff --git a/src/Helper/Drupal/FixtureFileTrait.php b/src/Helper/Drupal/FixtureFileTrait.php index 35fc4427..b5e132af 100644 --- a/src/Helper/Drupal/FixtureFileTrait.php +++ b/src/Helper/Drupal/FixtureFileTrait.php @@ -60,8 +60,8 @@ public function fixtureFileExpandEntityFields(string $entity_type, EntityStubInt continue; } - // A stub not yet parsed by 'entityParseFields()' still holds the raw - // compound cell as written in the Behat table + // A stub not yet parsed by 'entityLifecycleParseFields()' still holds + // the raw compound cell as written in the Behat table // (e.g. 'target_id:"foo.jpg", alt:"A"'). if (is_string($value) && $this->fixtureFileLooksLikeCompoundCell($value)) { $rewritten = $this->fixtureFileExpandCompoundCell($value, $fixture_path); @@ -73,7 +73,7 @@ public function fixtureFileExpandEntityFields(string $entity_type, EntityStubInt continue; } - // Parsed shapes produced by 'EntityFieldParser' or the legacy parser: + // The remaining shapes a stub value takes: // - scalar: 'foo.jpg' (treated as single-value) // - scalar list: ['foo.jpg', 'bar.jpg'] (multi-value) // - keyed record: ['target_id' => 'foo.jpg', 'alt' => 'A'] diff --git a/src/Helper/Web/RequestHeadersTrait.php b/src/Helper/Web/RequestHeadersTrait.php index 7dc75643..8ddf27f8 100644 --- a/src/Helper/Web/RequestHeadersTrait.php +++ b/src/Helper/Web/RequestHeadersTrait.php @@ -7,9 +7,8 @@ /** * Holds the request headers shared by the traits that issue HTTP requests. * - * 1 array is shared by every composing trait, so a header set by one trait - * is available to the trait that sends the request whether or not the - * context composes both. + * The array is a property of the context object, so a header set through one + * composing trait is read by every other composing trait in the same object. */ trait RequestHeadersTrait { diff --git a/src/Helper/Web/TableTransposeTrait.php b/src/Helper/Web/TableTransposeTrait.php index c19077f4..4dbcb904 100644 --- a/src/Helper/Web/TableTransposeTrait.php +++ b/src/Helper/Web/TableTransposeTrait.php @@ -81,7 +81,7 @@ public function tableTransposeVertical(TableNode $table): array { * Convert vertical format entities to horizontal TableNode. * * @param array> $entities - * Array of entity data arrays from transposeVerticalTable(). + * Array of entity data arrays from tableTransposeVertical(). * * @return \Behat\Gherkin\Node\TableNode * TableNode in horizontal format (first row is headers, subsequent rows diff --git a/src/Steps/Drupal/CacheTrait.php b/src/Steps/Drupal/CacheTrait.php index e12dbfaa..aff03e18 100644 --- a/src/Steps/Drupal/CacheTrait.php +++ b/src/Steps/Drupal/CacheTrait.php @@ -132,9 +132,6 @@ public function cacheRunCron(): void { /** * Get the cache bin used for the page cache. - * - * A consuming `FeatureContext` overrides this method when the site uses a - * custom internal page cache bin name. */ public function cacheGetPageCacheBin(): string { return $this->getOptionString('cache', 'page_cache_bin'); diff --git a/src/Steps/Drupal/ConfigOverrideTrait.php b/src/Steps/Drupal/ConfigOverrideTrait.php index 71c8d3ca..ee2ff11e 100644 --- a/src/Steps/Drupal/ConfigOverrideTrait.php +++ b/src/Steps/Drupal/ConfigOverrideTrait.php @@ -20,16 +20,20 @@ * runtime. They cannot be disabled from the Behat process because tests run * in a separate process from the system under test (SUT). * - * This trait signals the SUT - through a request header, a `$_SERVER` entry - * and an environment variable - that specific config objects should be read - * from their original (unoverridden) values. The SUT is responsible for - * reading that signal and calling `ImmutableConfig::getOriginal()` instead of - * `ImmutableConfig::get()` for the listed config names. + * This trait signals the SUT that specific config objects should be read + * from their original (unoverridden) values. The signal is a request header, + * a `$_SERVER` entry and an environment variable. + * + * The SUT is responsible for reading that signal and calling + * `ImmutableConfig::getOriginal()` instead of `ImmutableConfig::get()` for + * the listed config names. * * Activated by adding `@disable-config-override:CONFIG_NAME` tags to a * feature or scenario. Multiple tags are combined into a comma-separated - * list. Runs on every step because some steps reset headers set earlier in - * the scenario. + * list. + * + * The signal is applied before every step because some steps reset headers + * set earlier in the scenario. * * Limitations: * - The request header reaches the SUT only on a browser driver providing @@ -51,9 +55,9 @@ * @endcode * * The signal is also written to the request-header bag, so a trait that - * issues its own HTTP requests - `RestTrait` - carries it too. The bag is - * per context, so that reaches `RestTrait` only where one context composes - * both; the shipped `WebContext` and `DrupalContext` are separate objects. + * issues its own HTTP requests - `RestTrait` - carries it too. The bag is a + * property of the context object, so the signal reaches `RestTrait` when the + * same context composes both traits, as the shipped `DrupalContext` does. * * Example: * @code diff --git a/src/Steps/Drupal/ConfigTrait.php b/src/Steps/Drupal/ConfigTrait.php index d83e1959..f18586b8 100644 --- a/src/Steps/Drupal/ConfigTrait.php +++ b/src/Steps/Drupal/ConfigTrait.php @@ -22,13 +22,12 @@ * object's key holds, or contains, an expected value. Nested keys are * addressable with dotted notation (for example `page.front`). * - * Two families of assertions read the value differently: - * - The default steps read the STORED value via editable configuration, - * ignoring `settings.php` overrides. This is symmetric with the set steps - * and is what most setup-and-assert scenarios need. - * - The `effective` steps read the value through the config factory with - * module and `settings.php` overrides applied - the value the running site - * actually uses. + * 2 families of assertions read the value differently: + * - The default steps read the stored value, with module and `settings.php` + * overrides left unapplied. This is symmetric with the set steps and is + * what most setup-and-assert scenarios need. + * - The `effective` steps read the value with module and `settings.php` + * overrides applied: the value the running site uses. * * Values are compared by their stringified form, so `true`, `42` and JSON * arrays written in a step match their typed configuration counterparts. The @@ -36,8 +35,8 @@ * array values, searched recursively. * * Configuration objects touched by the set steps are snapshotted on first - * write and restored after the scenario: an existing object is reset to its - * original data and an object that did not exist is deleted. Skip the revert + * write and restored after the scenario. An existing object is reset to its + * original data, and an object that did not exist is deleted. Skip the revert * with `@behat-steps-skip:ConfigTrait`. * * @code @@ -245,9 +244,6 @@ public function configAssertEffectiveValueNotContains(string $name, string $key, /** * Read a stored configuration value, ignoring runtime overrides. * - * Editable configuration objects never carry module or `settings.php` - * overrides, so reading through one yields the value as saved. - * * @param string $name * The configuration object name. * @param string $key diff --git a/src/Steps/Drupal/DraggableviewsTrait.php b/src/Steps/Drupal/DraggableviewsTrait.php index c27e56b5..cd5a9481 100644 --- a/src/Steps/Drupal/DraggableviewsTrait.php +++ b/src/Steps/Drupal/DraggableviewsTrait.php @@ -25,7 +25,7 @@ trait DraggableviewsTrait { use QueryTrait; /** - * Save order of the Draggable Order items. + * Save the order of the Draggable Views items. * * @code * When I save the draggable views items of the view "draggableviews_demo" and the display "page_1" for the "article" content in the following order: diff --git a/src/Steps/Drupal/MenuTrait.php b/src/Steps/Drupal/MenuTrait.php index 5041199d..7aa37f0c 100644 --- a/src/Steps/Drupal/MenuTrait.php +++ b/src/Steps/Drupal/MenuTrait.php @@ -15,7 +15,7 @@ use Drupal\system\MenuInterface; /** - * Manage Drupal menu systems and menu link rendering. + * Manage Drupal menus and menu links. * * - Create and remove menus by label. * - Create and remove menu links, including parent-child hierarchies. diff --git a/src/Steps/Drupal/SearchApiTrait.php b/src/Steps/Drupal/SearchApiTrait.php index ed6e00a6..48ff0662 100644 --- a/src/Steps/Drupal/SearchApiTrait.php +++ b/src/Steps/Drupal/SearchApiTrait.php @@ -12,10 +12,11 @@ use Drupal\node\Entity\Node; /** - * Assert Drupal Search API with index and query operations. + * Run Drupal Search API indexing and cron hooks. * - * - Add content to an index + * - Add content to an index. * - Run indexing for a specific number of items. + * - Run the Search API and Search API Solr cron hooks. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ diff --git a/src/Steps/Drupal/StateTrait.php b/src/Steps/Drupal/StateTrait.php index 097b5d60..3241eb4c 100644 --- a/src/Steps/Drupal/StateTrait.php +++ b/src/Steps/Drupal/StateTrait.php @@ -168,10 +168,12 @@ public function stateAssertNotExists(string $name): void { /** * Read a state value, distinguishing stored NULL from a missing key. * - * Uses the underlying key/value store's `has()` so that a legitimately - * stored NULL is reported as existing. `\Drupal::state()->get()` cannot - * distinguish the two cases because it applies the `??` operator to - * the loaded value and returns the default for NULL. + * Existence is read through the backend's `stateExists()`, not from a NULL + * check on the value. A backend that can tell a stored NULL from an absent + * key then reports it as existing. + * + * `\Drupal::state()->get()` cannot tell the 2 cases apart: it applies the + * `??` operator to the loaded value and returns the default for NULL. * * @param string $name * The state key name. diff --git a/src/Steps/Web/AccessibilityTrait.php b/src/Steps/Web/AccessibilityTrait.php index 584837f6..c21855fb 100644 --- a/src/Steps/Web/AccessibilityTrait.php +++ b/src/Steps/Web/AccessibilityTrait.php @@ -108,7 +108,7 @@ trait AccessibilityTrait { * Populated as each scenario finalizes and consumed once by the static * `@AfterSuite` renderer. URLs are stored already formatted for display. * - * @var array}>}> + * @var array, results: array}>}> */ protected static array $accessibilityAggregate = []; @@ -1071,8 +1071,11 @@ protected function accessibilityRenderIssueList(string $heading, string $css_cla * * Violations below the threshold are recorded as passing cases carrying * the finding in ``, so they stay visible without failing the - * report. The `tests` and `failures` counts reflect the actual emitted - * `` elements, one per affected node. + * report. + * + * A violation emits 1 `` per affected node and a passed rule + * emits 1 `` with no node. `tests` counts every case and + * `failures` counts the cases carrying a ``. */ protected function accessibilityRenderJunit(): string { $threshold = $this->accessibilityEffectiveThreshold(); diff --git a/src/Steps/Web/CookieTrait.php b/src/Steps/Web/CookieTrait.php index fb80a1c0..0f236c53 100644 --- a/src/Steps/Web/CookieTrait.php +++ b/src/Steps/Web/CookieTrait.php @@ -12,8 +12,8 @@ * Verify and inspect browser cookies. * * - Assert cookie existence and values with exact or partial matching. - * - Support both WebDriver and BrowserKit browser drivers for test - * compatibility. + * - Read cookies through whichever browser driver provides the cookie + * capability. * * @phpstan-require-extends \DrevOps\BehatSteps\Behat\Context\WebRawContext */ diff --git a/src/Steps/Web/DateTrait.php b/src/Steps/Web/DateTrait.php index b6ea1c98..5e2ea21f 100644 --- a/src/Steps/Web/DateTrait.php +++ b/src/Steps/Web/DateTrait.php @@ -25,7 +25,7 @@ * * Examples: * - `[relative:-1 day]` converted to `1893456000` - * - `[relative:-1 day#Y-m-d]` converted to `2017-11-5` + * - `[relative:-1 day#Y-m-d]` converted to `2017-11-05` * * `dateRelativeProcessValue()` is public API. It and its helpers are static, * so a token resolves without a context instance. @@ -106,7 +106,7 @@ public function dateRelativeTransformTable(TableNode $table): TableNode { * * Examples: * [relative:-1 day] would be converted to 1893456000 - * [relative:-1 day#Y-m-d] would be converted to 2017-11-5 + * [relative:-1 day#Y-m-d] would be converted to 2017-11-05 * * @code * Given the following "article" content: @@ -123,8 +123,8 @@ public static function dateRelativeProcessValue(string $value, ?int $now = NULL) return $value; } - // An absent `now` truncates to the current minute, so every assertion in - // a long-running scenario resolves against the same base timestamp. + // An absent `now` truncates to the current minute, so tokens resolved + // within the same minute share a base timestamp. $now = $now ?: strtotime(date('Y-m-d H:i:00', static::dateGetNow())); $now = $now ?: NULL; diff --git a/src/Steps/Web/ElementTrait.php b/src/Steps/Web/ElementTrait.php index 1edddba6..c1062097 100644 --- a/src/Steps/Web/ElementTrait.php +++ b/src/Steps/Web/ElementTrait.php @@ -129,11 +129,11 @@ public function elementTriggerEvent(string $event, string $selector): void { } /** - * Scroll to an element with ID. + * Scroll to the element matching a CSS selector. * - * By default, scrolls the element to the center of the viewport. Override - * the elementGetScrollIntoViewCenter() method to return FALSE to use the - * behavior that aligns the element to the top of the viewport. + * The element is scrolled to the center of the viewport by default. An + * elementGetScrollIntoViewCenter() override returning FALSE aligns it to + * the top of the viewport instead. * * @code * When I scroll to the element "#footer" @@ -342,7 +342,7 @@ public function elementAssertExistsWithAttributeContainingValue(string $selector } /** - * Assert an element with selector and attribute with a value exists. + * Assert an element with selector and attribute with a value does not exist. * * @code * Then the element "#main-content" with the attribute "class" and the value "hidden" should not exist diff --git a/src/Steps/Web/FieldTrait.php b/src/Steps/Web/FieldTrait.php index 2e21e2cd..bb5c2fe5 100644 --- a/src/Steps/Web/FieldTrait.php +++ b/src/Steps/Web/FieldTrait.php @@ -171,12 +171,11 @@ public function fieldFillMultiValue(string $field, TableNode $table): void { $page = $this->getSession()->getPage(); - // Drupal multi-value widgets wrap the field rows (a table) and the - // "Add another item" button in an outer container identified by - // `data-drupal-selector="edit--wrapper"`. The title can appear - // in a nested