Repository: yiisoft/yii2

Issue:
ErrorHandler::convertExceptionToError() Passing E_USER_ERROR to trigger_error() is deprecated since 8.4
ErrorHandler::convertExceptionToError() converts exception to error by triggering E_USER_ERROR.

PHP documentation says:

> Warning

> Passing [E_USER_ERROR](https://www.php.net/manual/en/errorfunc.constants.php#constant.e-user-error) as the error_level is now deprecated. Throw an [Exception](https://www.php.net/manual/en/class.exception.php) or call [exit()](https://www.php.net/manual/en/function.exit.php) instead.

Hints from issue comments:
@terabytesoftw: Hi, i think it can be solved this way, while maintaining backward compatibility.

```php
public static function convertExceptionToError($exception)
{
    if (PHP_VERSION_ID >= 80400) {
        $exceptionMessage = static::convertExceptionToString($exception);

        Yii::error($exceptionMessage, 'E_USER_ERROR');

        if (PHP_SAPI !== 'cli') {
            echo $exceptionMessage;
        } else {
            fwrite(STDERR, $exceptionMessage . PHP_EOL);
        }

        exit(1);
    } else {
        trigger_error(static::convertExceptionToString($exception), E_USER_ERROR);
    }
}
```

@samdark @mtangoo @bizley @rob006 @schmunk42 @xepozz @rob006: You can throw exceptions in `__toString()` since PHP 7.4. It looks like this method is used only in two places. I would rather deprecate this method, call `trigger_error()` directly in this two places only for PHP 7.3, and re-throw exception for higher versions of PHP. @terabytesoftw: Yes, If it is used only in two `to_string()`, but the deprecation is only in `PHP` `8.4`, so it would make sense, not to deprecate the method, and only call it if it is `PHP` < `8.4`, so we would not do BC.

```php
public function __toString()
{
    // __toString cannot throw exception
    // use trigger_error to bypass this limitation
    try {
        return $this->toString();
    } catch (\Throwable $e) {
        if (PHP_VERSION_ID < 80400) {
            trigger_error(ErrorHandler::convertExceptionToString($e), E_USER_ERROR);

            return '';
        }

        throw $e;
    }
}
````

Or throw exception in `convertExceptionToError()` without depreciating the method..@rob006: I think that it is a bug that we convert exceptions to `E_USER_ERROR` for PHP 7.4 and above. It just makes handling such exceptions harder than it should be.@terabytesoftw: Well, we can use only trigger_error for `PHP 7.3`, we updated the phpdoc that the best way is throw exception, without needing to depreciate the method, so existing extensions or app will continue to work and if you use `PHP 8.4` it will also work.@rob006: I don't think it is worth to have `convertExceptionToError()` method that doesn't do what the name says in most of the cases (according to Packagist PHP 7.3 has less than 4% of installs). For Yii 2.2 that method would be completely pointless, so IMO the best course of action would be to deprecate it in 2.0 and remove in 2.2.@terabytesoftw: > I don't think it is worth to have `convertExceptionToError()` method that doesn't do what the name says in most of the cases (according to Packagist PHP 7.3 has less than 4% of installs). For Yii 2.2 that method would be completely pointless, so IMO the best course of action would be to deprecate it in 2.0 and remove in 2.2.

So should we remove support for PHP 7.3?@rob006: In 2.0.x (`master` branch):

1. Deprecate `convertExceptionToError()` and don't use it in the framework. 
2. In places where it was used, call `trigger_error()` directly for `PHP_VERSION_ID < 70400` and re-throw exception for other cases.

In 2.2.x:

1. Remove `convertExceptionToError()`. 
2. Remove try-catch and `trigger_error()` in these two places where we had this hack for exceptions in `__toString()` - they're no longer needed, since 2.2 requires PHP 8.1 which handles exceptions in `__toString()` just fine.

Solution patch:
diff --git a/.codeclimate.yml b/.codeclimate.yml
deleted file mode 100644
index ffdbda961..000000000
--- a/.codeclimate.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-engines:
-  duplication:
-    enabled: true
-    config:
-      languages:
-      - javascript
-      - php
-  eslint:
-    enabled: true
-  fixme:
-    enabled: true
-  phpmd:
-    enabled: true
-    config:
-      rulesets: "codesize,design,unusedcode,tests/data/codeclimate/phpmd_ruleset.xml"
-ratings:
-  paths:
-  - "**.js"
-  - "**.php"
-exclude_paths:
-- tests/
-- build/
-- docs/
-- framework/messages/
-
-checks:
-  file-lines:
-    enabled: false
-  method-count:
-    enabled: false
-  method-lines:
-    enabled: false
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index ec71047b9..dfc43e5f9 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -4,3 +4,5 @@ b5f8a4dc22d5f8188405a2099d85fc154226c9b2
 ba0ab403b52124c941dbeb46fbd9efdc12252a5d
 # Coding style fixes
 9d327baa8b2c80b53d4d405678f03e6b89ff6e38
+# Add visibility for all class elements
+c82da8dc829d557e82b12edbed9e37003dfcc9a3
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index de71e1bbd..6fcc8d476 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml
index 87f264742..b2999bff1 100644
--- a/.github/workflows/ci-mariadb.yml
+++ b/.github/workflows/ci-mariadb.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-mssql.yml b/.github/workflows/ci-mssql.yml
index 9d4c31a9d..81f60470f 100644
--- a/.github/workflows/ci-mssql.yml
+++ b/.github/workflows/ci-mssql.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml
index 89ce4e8f2..e72be5b7a 100644
--- a/.github/workflows/ci-mysql.yml
+++ b/.github/workflows/ci-mysql.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml
index acd75b397..d9732dca7 100644
--- a/.github/workflows/ci-node.yml
+++ b/.github/workflows/ci-node.yml
@@ -4,7 +4,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-oracle.yml b/.github/workflows/ci-oracle.yml
index d44169fcc..930ecbc1f 100644
--- a/.github/workflows/ci-oracle.yml
+++ b/.github/workflows/ci-oracle.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-pgsql.yml b/.github/workflows/ci-pgsql.yml
index 387630580..0c3df4102 100644
--- a/.github/workflows/ci-pgsql.yml
+++ b/.github/workflows/ci-pgsql.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/ci-sqlite.yml b/.github/workflows/ci-sqlite.yml
index 9e55c12cd..cca5da51c 100644
--- a/.github/workflows/ci-sqlite.yml
+++ b/.github/workflows/ci-sqlite.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 861d649c6..225649bc3 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
index d19ae0d12..d5b3a4d0e 100644
--- a/.github/workflows/static.yml
+++ b/.github/workflows/static.yml
@@ -9,7 +9,6 @@ on:
   pull_request: &ignore-paths
     paths-ignore:
       - ".appveyor.yml"
-      - ".codeclimate.yml"
       - ".dockerignore"
       - ".editorconfig"
       - ".eslintignore"
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index 198155c8e..000000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,91 +0,0 @@
-checks:
-  php: true
-
-filter:
-  paths:
-    - framework/
-
-  excluded_paths:
-    - vendor/
-
-  dependency_paths:
-    - vendor/
-
-build:
-  image: default-bionic
-
-  environment:
-    php:
-      version: 8.2
-      ini:
-        xdebug.mode: coverage
-        memory_limit: -1
-
-  nodes:
-    analysis:
-      tests:
-        override:
-          - php-scrutinizer-run
-
-    phpunit:
-      dependencies:
-        override:
-          - composer self-update
-          - composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
-
-      services:
-        db-mysql:
-          image: mysql:5.7
-
-          # Define any additional environment variables that are needed by the service.
-          env:
-            MYSQL_ALLOW_EMPTY_PASSWORD: 1
-            MYSQL_ROOT_PASSWORD: root
-            MYSQL_DATABASE: yiitest
-
-          # We automatically forward these ports from your localhost to the service's port.
-          # Alternatively, you can also access the service on the "$SERVICE_SOME_NAME_IP"
-          # environment variable.
-          ports:
-            # Forward 127.0.0.1:12345 -> SERVICE_IP:12345
-            - 3306
-
-          # If your service writes data to disk like most databases do, you can significantly
-          # speed up tests by mounting a ramdisk at those paths.
-          ramdisks:
-            - /var/lib/data
-
-        db-pgsql:
-          image: postgres:14
-
-          # Define any additional environment variables that are needed by the service.
-          env:
-            POSTGRES_USER: postgres
-            POSTGRES_PASSWORD: postgres
-            POSTGRES_DB: yiitest
-
-          # We automatically forward these ports from your localhost to the service's port.
-          # Alternatively, you can also access the service on the "$SERVICE_SOME_NAME_IP"
-          # environment variable.
-          ports:
-            # Forward 127.0.0.1:12345 -> SERVICE_IP:12345
-            - 5432
-
-          # If your service writes data to disk like most databases do, you can significantly
-          # speed up tests by mounting a ramdisk at those paths.
-          ramdisks:
-            - /var/lib/data
-
-        memcached:
-          image: memcached
-
-      tests:
-        before:
-          - sudo locale-gen fr_FR.UTF-8
-
-        override:
-          - command: ./vendor/bin/phpunit --verbose --exclude-group db,wincache --colors=always --coverage-clover ./coverage.xml
-            on_node: 1
-            coverage:
-              file: coverage.xml
-              format: php-clover
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index c35e1f77c..3257be4b9 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -28,9 +28,11 @@ Yii Framework 2 Change Log
 - Bug #20518: `PHP` 8.4 fixes for implicit nullability deprecation (terabytesoftw)
 - Enh #20537: Enhance `label()` method in `ActiveField` with `tag` option and fix `labelOptions` for `checkbox`/`radio` (terabytesoftw)
 - Bug #20547: `PHP` 8.4 fixes for implicit nullability deprecation (terabytesoftw)
+- Bug #20573: Fix `checkbox`/`radio` label rendering when `enclosedByLabel` is `false` and raise code coverage `100%` in `ActiveField::class` (terabytesoftw)
 
 2.0.54 under development
 ------------------------
+
 - Bug #20542: Fix `Formatter` working with more complex ICU unit data structure (OndrejVasicek)
 - Bug #20483: Fix `CompositeAuth` making bad assumptions on `AuthInterface` implementations (sammousa)
 - Bug #20432: Fix PHPStan/Psalm annotations for `ActiveQuery::asArray` (max-s-lab)
@@ -52,6 +54,7 @@ Yii Framework 2 Change Log
 - Bug #20489: Replace deprecated `strftime` with `date` in `YiiRequirementChecker` (max-s-lab)
 - Bug #20494: Fix `PHPdoc`, add `PHPStan/Psalm` annotations for `authMethods` property in `CompositeAuth` class (terabytesoftw)
 - Bug #20485: Fix error `Cannot unset string offsets` in `yii\di\Instance:ensure(['__class' => ...], 'some\class\name')` (max-s-lab)
+- Enh #20047: Throw exception when fixture not found rather than ignoring (borgou)
 - Enh #20505: `ArrayDataProvider` key handling with flexible path support (fetus-hina)
 - Bug #20508: Fix PHPDoc, add PHPStan/Psalm annotations for `yii\web\CookieCollection::getIterator`. Add missing `@property` annotation in `yii\base\Model` (max-s-lab)
 - Bug #20513: Fix code examples in PHPDoc (max-s-lab)
@@ -68,6 +71,10 @@ Yii Framework 2 Change Log
 - Bug #20541: Remove deprecated caching components: `XCache` and `ZendDataCache`, and update related tests and documentation (terabytesoftw)
 - Bug #20548: Fix PHP `8.5` `null` array offset deprecation warnings (terabytesoftw)
 - Enh #19526: Add the `convertIniSizeToBytes` method to `BaseStringHelper` (max-s-lab)
+- Bug #20570: Fix `@var` annotation for `UrlManager::$cache` (max-s-lab)
+- Bug #20571: Fix `@var` annotation for `yii\web\Response::$stream` (max-s-lab)
+- Bug #20569: Fix `@param` annotation for `$default` in `HeaderCollection::get` (max-s-lab)
+- Bug #20576: Fix `@var` annotation for `StringValidator::$length` (max-s-lab)
 
 
 2.0.53 June 27, 2025
@@ -175,7 +182,6 @@ Yii Framework 2 Change Log
 - Enh #20171: Support JSON columns for MariaDB 10.4 or higher (@terabytesoftw)
 - New #20137: Added `yii\caching\CallbackDependency` to allow using a callback to determine if a cache dependency is still valid (laxity7)
 
-
 2.0.49.2 October 12, 2023
 -------------------------
 
diff --git a/framework/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php
index 4f1d02246..c8b09bd4b 100644
--- a/framework/console/controllers/FixtureController.php
+++ b/framework/console/controllers/FixtureController.php
@@ -483,6 +483,8 @@ class FixtureController extends Controller
                 $config[] = $fullClassName;
             } elseif (class_exists($fullClassName . 'Fixture')) {
                 $config[] = $fullClassName . 'Fixture';
+            } else {
+                throw new Exception('Neither fixture "' . $fullClassName . '" nor "' . $fullClassName . 'Fixture" was found.');
             }
         }
 
diff --git a/framework/i18n/MessageFormatter.php b/framework/i18n/MessageFormatter.php
index 516331270..5df9247ca 100644
--- a/framework/i18n/MessageFormatter.php
+++ b/framework/i18n/MessageFormatter.php
@@ -204,7 +204,7 @@ class MessageFormatter extends Component
      * @param array $givenParams The array of values to insert into the format string.
      * @param array $resultingParams Modified array of parameters.
      * @param array $map
-     * @return string The pattern string with placeholders replaced.
+     * @return string|false The pattern string with placeholders replaced.
      */
     private function replaceNamedArguments($pattern, $givenParams, &$resultingParams = [], &$map = [])
     {
diff --git a/framework/validators/StringValidator.php b/framework/validators/StringValidator.php
index 463cc5cb0..ce9cdd4b2 100644
--- a/framework/validators/StringValidator.php
+++ b/framework/validators/StringValidator.php
@@ -21,7 +21,7 @@ use yii\helpers\Json;
 class StringValidator extends Validator
 {
     /**
-     * @var int|array specifies the length limit of the value to be validated.
+     * @var int|array|null specifies the length limit of the value to be validated.
      * This can be specified in one of the following forms:
      *
      * - an integer: the exact length that the value should be of;
diff --git a/framework/web/HeaderCollection.php b/framework/web/HeaderCollection.php
index 64cae459b..57ddd6dfe 100644
--- a/framework/web/HeaderCollection.php
+++ b/framework/web/HeaderCollection.php
@@ -67,7 +67,7 @@ class HeaderCollection extends BaseObject implements \IteratorAggregate, \ArrayA
     /**
      * Returns the named header(s).
      * @param string $name the name of the header to return
-     * @param mixed $default the value to return in case the named header does not exist
+     * @param string|array|null $default the value to return in case the named header does not exist
      * @param bool $first whether to only return the first header of the specified name.
      * If false, all headers of the specified name will be returned.
      * @return string|array|null the named header(s). If `$first` is true, a string will be returned;
diff --git a/framework/web/Request.php b/framework/web/Request.php
index 8918187c2..67d3201ff 100644
--- a/framework/web/Request.php
+++ b/framework/web/Request.php
@@ -87,7 +87,6 @@ use yii\validators\IpValidator;
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
- * @SuppressWarnings(PHPMD.SuperGlobals)
  */
 class Request extends \yii\base\Request
 {
diff --git a/framework/web/Response.php b/framework/web/Response.php
index 29ebbb48a..a8dde71fb 100644
--- a/framework/web/Response.php
+++ b/framework/web/Response.php
@@ -134,7 +134,7 @@ class Response extends \yii\base\Response
      */
     public $content;
     /**
-     * @var resource|array|callable the stream to be sent. This can be a stream handle or an array of stream handle,
+     * @var resource|array|callable|null the stream to be sent. This can be a stream handle or an array of stream handle,
      * the begin position and the end position. Alternatively it can be set to a callable, which returns
      * (or [yields](https://www.php.net/manual/en/language.generators.syntax.php)) an array of strings that should
      * be echoed and flushed out one by one.
@@ -239,7 +239,7 @@ class Response extends \yii\base\Response
      */
     private $_statusCode = 200;
     /**
-     * @var HeaderCollection
+     * @var HeaderCollection|null
      */
     private $_headers;
 
@@ -936,6 +936,9 @@ class Response extends \yii\base\Response
         return $this->redirect(Yii::$app->getRequest()->getUrl() . $anchor);
     }
 
+    /**
+     * @var CookieCollection|null
+     */
     private $_cookies;
 
     /**
diff --git a/framework/web/UploadedFile.php b/framework/web/UploadedFile.php
index 7e5f82459..9cfe95b90 100644
--- a/framework/web/UploadedFile.php
+++ b/framework/web/UploadedFile.php
@@ -69,7 +69,7 @@ class UploadedFile extends BaseObject
      */
     private $_tempResource;
     /**
-     * @var array[]
+     * @var array[]|null
      */
     private static $_files;
 
diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php
index d7d161b51..4d8558c39 100644
--- a/framework/web/UrlManager.php
+++ b/framework/web/UrlManager.php
@@ -119,7 +119,7 @@ class UrlManager extends Component
      */
     public $routeParam = 'r';
     /**
-     * @var CacheInterface|array|string|bool the cache object or the application component ID of the cache object.
+     * @var CacheInterface|array|string|bool|null the cache object or the application component ID of the cache object.
      * This can also be an array that is used to create a [[CacheInterface]] instance in case you do not want to use
      * an application component.
      * Compiled URL rules will be cached through this cache object, if it is available.
diff --git a/framework/widgets/ActiveField.php b/framework/widgets/ActiveField.php
index 08f026b52..708dd6b9d 100644
--- a/framework/widgets/ActiveField.php
+++ b/framework/widgets/ActiveField.php
@@ -170,13 +170,7 @@ class ActiveField extends Component
      */
     public function __toString()
     {
-        // __toString cannot throw exception
-        // use trigger_error to bypass this limitation
-        try {
-            return $this->render();
-        } catch (\Throwable $e) {
-            throw $e;
-        }
+        return $this->render();
     }
 
     /**
@@ -588,9 +582,11 @@ class ActiveField extends Component
         $this->addAriaAttributes($options);
         $this->adjustLabelFor($options);
 
-        $this->parts['{label}'] = '';
-
-        $options = $enclosedByLabel ? $options : $this->generateLabel($options);
+        if ($enclosedByLabel) {
+            $this->parts['{label}'] = '';
+        } else {
+            $options = $this->generateLabel($options);
+        }
 
         $this->parts['{input}'] = Html::activeRadio($this->model, $this->attribute, $options);
 
@@ -641,9 +637,11 @@ class ActiveField extends Component
         $this->addAriaAttributes($options);
         $this->adjustLabelFor($options);
 
-        $this->parts['{label}'] = '';
-
-        $options = $enclosedByLabel ? $options : $this->generateLabel($options);
+        if ($enclosedByLabel) {
+            $this->parts['{label}'] = '';
+        } else {
+            $options = $this->generateLabel($options);
+        }
 
         $this->parts['{input}'] = Html::activeCheckbox($this->model, $this->attribute, $options);
 
@@ -1003,27 +1001,31 @@ class ActiveField extends Component
      */
     protected function generateLabel(array $options): array
     {
-        if (isset($options['label']) && $options['label'] !== false && $this->parts['{label}'] === '') {
-            $tag = false;
+        if (isset($options['label'])) {
+            if ($options['label'] === false) {
+                $this->parts['{label}'] = '';
+            } elseif (($this->parts['{label}'] ?? '') === '') {
+                $tag = false;
 
-            if (!empty($options['labelOptions'])) {
-                $tag = $options['labelOptions']['tag'] ?? 'label';
+                if (!empty($options['labelOptions'])) {
+                    $tag = $options['labelOptions']['tag'] ?? 'label';
 
-                unset($options['labelOptions']['tag']);
+                    unset($options['labelOptions']['tag']);
 
-                $this->labelOptions = array_merge($this->labelOptions, $options['labelOptions']);
-            }
+                    $this->labelOptions = array_merge($this->labelOptions, $options['labelOptions']);
+                }
 
-            if ($tag === false) {
-                $this->parts['{label}'] = $options['label'];
-            } elseif ($tag === 'label') {
-                $this->parts['{label}'] = Html::activeLabel(
-                    $this->model,
-                    $this->attribute,
-                    array_merge(['label' => $options['label']], $this->labelOptions),
-                );
-            } else {
-                $this->parts['{label}'] = Html::tag($tag, $options['label'], $this->labelOptions);
+                if ($tag === false) {
+                    $this->parts['{label}'] = $options['label'];
+                } elseif ($tag === 'label') {
+                    $this->parts['{label}'] = Html::activeLabel(
+                        $this->model,
+                        $this->attribute,
+                        array_merge(['label' => $options['label']], $this->labelOptions),
+                    );
+                } else {
+                    $this->parts['{label}'] = Html::tag($tag, $options['label'], $this->labelOptions);
+                }
             }
         }
 
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index b3e28f805..a3266bd9e 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -24,3 +24,360 @@ parameters:
 			message: "#^Variable \\$error in isset\\(\\) always exists and is always null\\.$#"
 			count: 1
 			path: framework/helpers/BaseConsole.php
+
+		-
+			message: "#^Unsafe call to private method yii\\\\helpers\\\\BaseConsole::getProgressbarWidth\\(\\) through static\\:\\:\\.$#"
+			count: 1
+			path: framework/helpers/BaseConsole.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$modelClass\\.$#"
+			count: 1
+			path: framework/grid/DataColumn.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\Schema\\:\\:getViewNames\\(\\)\\.$#"
+			count: 1
+			path: framework/db/pgsql/QueryBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 1
+			path: framework/db/pgsql/JsonExpressionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 2
+			path: framework/db/pgsql/ArrayExpressionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getDimension\\(\\)\\.$#"
+			count: 1
+			path: framework/db/pgsql/ArrayExpressionBuilder.php
+
+		-
+			message: "#^PHPDoc tag @param for parameter \\$condition with type yii\\\\db\\\\ExpressionInterface is not subtype of native type yii\\\\db\\\\conditions\\\\InCondition\\.$#"
+			count: 1
+			path: framework/db/oci/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 1
+			path: framework/db/mysql/JsonExpressionBuilder.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ColumnSchema\\:\\:\\$isComputed\\.$#"
+			count: 1
+			path: framework/db/mssql/Schema.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ColumnSchema\\:\\:defaultPhpTypecast\\(\\)\\.$#"
+			count: 1
+			path: framework/db/mssql/Schema.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\Schema\\:\\:getViewNames\\(\\)\\.$#"
+			count: 1
+			path: framework/db/mssql/QueryBuilder.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ColumnSchema\\:\\:\\$isComputed\\.$#"
+			count: 1
+			path: framework/db/mssql/QueryBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/SimpleConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/SimpleConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/SimpleConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getCondition\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/NotConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/LikeConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/LikeConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/LikeConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getEscapingReplacements\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/LikeConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValues\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method Traversable\\:\\:rewind\\(\\)\\.$#"
+			count: 2
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method Traversable\\:\\:current\\(\\)\\.$#"
+			count: 2
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\conditions\\\\ConditionInterface\\:\\:getColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/InConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getHash\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/HashConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/ExistsConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getQuery\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/ExistsConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/ConjunctionConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getExpressions\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/ConjunctionConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getIntervalStart\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getIntervalEnd\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getOperator\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenColumnsConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getIntervalStartColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenColumnsConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getIntervalEndColumn\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenColumnsConditionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:getValue\\(\\)\\.$#"
+			count: 1
+			path: framework/db/conditions/BetweenColumnsConditionBuilder.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ExpressionInterface\\:\\:\\$params\\.$#"
+			count: 1
+			path: framework/db/ExpressionBuilder.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ExpressionInterface\\:\\:__toString\\(\\)\\.$#"
+			count: 1
+			path: framework/db/ExpressionBuilder.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$via\\.$#"
+			count: 4
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$link\\.$#"
+			count: 16
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$multiple\\.$#"
+			count: 2
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$indexBy\\.$#"
+			count: 1
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$modelClass\\.$#"
+			count: 3
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ActiveRecordInterface\\:\\:attributeLabels\\(\\)\\.$#"
+			count: 1
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ActiveRecordInterface\\:\\:isRelationPopulated\\(\\)\\.$#"
+			count: 2
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ActiveRecordInterface\\:\\:attributeHints\\(\\)\\.$#"
+			count: 1
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ActiveQueryInterface\\:\\:findWith\\(\\)\\.$#"
+			count: 1
+			path: framework/db/BaseActiveRecord.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$multiple\\.$#"
+			count: 3
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$link\\.$#"
+			count: 4
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^PHPDoc tag \\@var for variable \\$viaQuery has invalid type yii\\\\db\\\\ActiveQueryTrait\\.$#"
+			count: 1
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Access to property \\$asArray on an unknown class yii\\\\db\\\\ActiveQueryTrait\\.$#"
+			count: 1
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Access to property \\$primaryModel on an unknown class yii\\\\db\\\\ActiveQueryTrait\\.$#"
+			count: 2
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Call to method asArray\\(\\) on an unknown class yii\\\\db\\\\ActiveQueryTrait\\.$#"
+			count: 1
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Call to method populateRelation\\(\\) on an unknown class yii\\\\db\\\\ActiveQueryTrait\\.$#"
+			count: 1
+			path: framework/db/ActiveRelationTrait.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$primaryModel\\.$#"
+			count: 1
+			path: framework/db/ActiveQueryTrait.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$with\\.$#"
+			count: 1
+			path: framework/db/ActiveQueryTrait.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$modelClass\\.$#"
+			count: 1
+			path: framework/db/ActiveQuery.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\db\\\\ActiveQueryInterface\\:\\:buildJoinWith\\(\\)\\.$#"
+			count: 1
+			path: framework/db/ActiveQuery.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\db\\\\ActiveQueryInterface\\:\\:\\$modelClass\\.$#"
+			count: 2
+			path: framework/data/ActiveDataProvider.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\base\\\\BaseObject\\&yii\\\\db\\\\MigrationInterface\\:\\:\\$compact\\.$#"
+			count: 1
+			path: framework/console/controllers/BaseMigrateController.php
+
+		-
+			message: "#^PHPDoc tag \\@param references unknown parameter\\: \\$args$#"
+			count: 2
+			path: framework/console/Controller.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\base\\\\Component\\:\\:isAttributeChanged\\(\\)\\.$#"
+			count: 1
+			path: framework/behaviors/SluggableBehavior.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\base\\\\Component\\:\\:formName\\(\\)\\.$#"
+			count: 1
+			path: framework/behaviors/OptimisticLockBehavior.php
+
+		-
+			message: "#^Access to an undefined property yii\\\\base\\\\Component\\:\\:\\$view\\.$#"
+			count: 2
+			path: framework/behaviors/CacheableWidgetBehavior.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\base\\\\Model\\:\\:canSetOldAttribute\\(\\)\\.$#"
+			count: 1
+			path: framework/behaviors/AttributeTypecastBehavior.php
+
+		-
+			message: "#^Call to an undefined method yii\\\\base\\\\Model\\:\\:setOldAttribute\\(\\)\\.$#"
+			count: 1
+			path: framework/behaviors/AttributeTypecastBehavior.php
+
+		-
+			message: '#^Call to an undefined method ReflectionType\:\:getName\(\)\.$#'
+			identifier: method.notFound
+			count: 2
+			path: framework/di/Container.php
+
+		-
+			message: '#^Call to an undefined method ReflectionType\:\:isBuiltin\(\)\.$#'
+			identifier: method.notFound
+			count: 1
+			path: framework/di/Container.php
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
index 3f06ff7fe..cddd5efa5 100644
--- a/phpstan.dist.neon
+++ b/phpstan.dist.neon
@@ -2,7 +2,7 @@ includes:
     - phpstan-baseline.neon
 
 parameters:
-    level: 1
+    level: 2
 
     paths:
         - build



Test patch:
diff --git a/tests/TestCase.php b/tests/TestCase.php
index d9961eeb2..8bac11cf4 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -263,4 +263,12 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
             Yii::$app->db->password = $database['password'] ?? null;
         }
     }
+
+    /**
+     * Normalizes HTML string by removing redundant blank lines.
+     */
+    protected function normalizeHTML(string $html): string
+    {
+        return preg_replace('/^\h*\n/m', '', trim($html));
+    }
 }
diff --git a/tests/framework/widgets/ActiveFieldTest.php b/tests/framework/widgets/ActiveFieldTest.php
index 63f2687a0..971f4d7d0 100644
--- a/tests/framework/widgets/ActiveFieldTest.php
+++ b/tests/framework/widgets/ActiveFieldTest.php
@@ -43,6 +43,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
     protected function setUp(): void
     {
         parent::setUp();
+
         // dirty way to have Request object not throwing exception when running testHomeLinkNull()
         $_SERVER['SCRIPT_FILENAME'] = 'index.php';
         $_SERVER['SCRIPT_NAME'] = 'index.php';
@@ -53,6 +54,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
         Yii::setAlias('@testWebRoot', '@yiiunit/data/web');
 
         $this->helperModel = new ActiveFieldTestModel(['attributeName']);
+
         ob_start();
         $this->helperForm = ActiveForm::begin(['action' => '/something', 'enableClientScript' => false]);
         ActiveForm::end();
@@ -60,7 +62,9 @@ class ActiveFieldTest extends \yiiunit\TestCase
 
         $this->activeField = new ActiveFieldExtend(true);
         $this->activeField->form = $this->helperForm;
+
         $this->activeField->form->setView($this->getView());
+
         $this->activeField->model = $this->helperModel;
         $this->activeField->attribute = $this->attributeName;
     }
@@ -77,6 +81,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -87,20 +92,23 @@ class ActiveFieldTest extends \yiiunit\TestCase
     {
         // field will be the html of the model's attribute wrapped with the return string below.
         $field = $this->attributeName;
-        $content = static fn (string $field): string => "<div class=\"custom-container\"> $field </div>";
+        $content = static fn (string $field): string => "<div class=\"custom-container\">\n$field\n</div>";
 
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="form-group field-activefieldtestmodel-attributename">
-            <div class="custom-container"> <div class="form-group field-activefieldtestmodel-attributename">
+            <div class="custom-container">
+            <div class="form-group field-activefieldtestmodel-attributename">
             <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
             <input type="text" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[{$this->attributeName}]">
             <div class="hint-block">Hint for attributeName attribute</div>
             <div class="help-block"></div>
-            </div> </div>
+            </div>
+            </div>
             </div>
             HTML,
             $this->activeField->render($content),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -121,6 +129,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -133,6 +142,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <div class="form-group field-activefieldtestmodel-attributename has-error">
             HTML,
             $this->activeField->begin(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -145,6 +155,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <div class="form-group field-activefieldtestmodel-attributename required">
             HTML,
             $this->activeField->begin(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -158,55 +169,71 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <div class="form-group field-activefieldtestmodel-attributename required has-error">
             HTML,
             $this->activeField->begin(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testBegin(): void
     {
-        $expectedValue = '<article class="form-group field-activefieldtestmodel-attributename">';
         $this->activeField->options['tag'] = 'article';
-        $actualValue = $this->activeField->begin();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertSame(
+            <<<HTML
+            <article class="form-group field-activefieldtestmodel-attributename">
+            HTML,
+            $this->activeField->begin(),
+            'Rendered HTML does not match expected output',
+        );
 
-        $expectedValue = '';
         $this->activeField->options['tag'] = null;
-        $actualValue = $this->activeField->begin();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertEmpty(
+            $this->activeField->begin(),
+            "Failed asserting that 'begin()' does not render.",
+        );
 
-        $expectedValue = '';
         $this->activeField->options['tag'] = false;
-        $actualValue = $this->activeField->begin();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertEmpty(
+            $this->activeField->begin(),
+            "Failed asserting that 'begin()' does not render.",
+        );
     }
 
     public function testEnd(): void
     {
-        $expectedValue = '</div>';
-        $actualValue = $this->activeField->end();
-
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertSame(
+            <<<HTML
+            </div>
+            HTML,
+            $this->activeField->end(),
+            'Rendered HTML does not match expected output',
+        );
 
         // other tag
-        $expectedValue = '</article>';
         $this->activeField->options['tag'] = 'article';
-        $actualValue = $this->activeField->end();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertSame(
+            <<<HTML
+            </article>
+            HTML,
+            $this->activeField->end(),
+            'Rendered HTML does not match expected output',
+        );
 
-        $expectedValue = '';
         $this->activeField->options['tag'] = false;
-        $actualValue = $this->activeField->end();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertEmpty(
+            $this->activeField->end(),
+            "Failed asserting that 'end()' does not render.",
+        );
 
-        $expectedValue = '';
         $this->activeField->options['tag'] = null;
-        $actualValue = $this->activeField->end();
 
-        $this->assertSame($expectedValue, $actualValue);
+        $this->assertEmpty(
+            $this->activeField->end(),
+            "Failed asserting that 'end()' does not render.",
+        );
     }
 
     public function testLabel(): void
@@ -218,7 +245,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -239,7 +266,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="override-class" data-test="inherited-data" for="activefieldtestmodel-attributename">Test Label</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -255,7 +282,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">{$label}</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->label(null, ['label' => $paramLabel]);
@@ -265,7 +292,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">{$paramLabel}</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->label();
@@ -275,7 +302,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -287,7 +314,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
 
         $this->assertEmpty(
             $this->activeField->parts['{label}'],
-            'Failed asserting that label does not render.',
+            "Failed asserting that 'label()' does not render.",
         );
     }
 
@@ -299,7 +326,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
 
         $this->assertEmpty(
             $this->activeField->parts['{label}'],
-            'Failed asserting that label does not render.',
+            "Failed asserting that 'label()' does not render.",
         );
     }
 
@@ -314,7 +341,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label for="activefieldtestmodel-attributename">Override Label</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -329,7 +356,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">{$label}</label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -342,7 +369,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename"></label>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -363,7 +390,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <h3 class="custom-class">{$label}</h3>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->label(
@@ -380,7 +407,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <h3 class="custom-class">{$label}</h3>
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -401,7 +428,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             {$label}
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->label(
@@ -418,7 +445,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             {$label}
             HTML,
             $this->activeField->parts['{label}'],
-            'Failed asserting that label renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -431,13 +458,16 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
             HTML,
             $this->activeField->parts['{label}'],
+            'Rendered HTML does not match expected output',
        );
 
         // label = false
-        $expectedValue = '';
         $this->activeField->label(false);
 
-        $this->assertSame($expectedValue, $this->activeField->parts['{label}']);
+        $this->assertEmpty(
+            $this->activeField->parts['{label}'],
+            "Failed asserting that 'label()' does not render.",
+        );
 
         // $label = 'Label Name'
         $label = 'Label Name';
@@ -448,12 +478,14 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <label class="control-label" for="activefieldtestmodel-attributename">{$label}</label>
             HTML,
             $this->activeField->parts['{label}'],
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testTabularInputErrors(): void
     {
-        $this->activeField->attribute = '[0]'.$this->attributeName;
+        $this->activeField->attribute = "[0]$this->attributeName";
+
         $this->helperModel->addError($this->attributeName, 'Error Message');
 
         $this->assertEqualsWithoutLE(
@@ -461,29 +493,22 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <div class="form-group field-activefieldtestmodel-0-attributename has-error">
             HTML,
             $this->activeField->begin(),
+            'Rendered HTML does not match expected output',
         );
     }
 
-    public static function hintDataProvider(): array
-    {
-        return [
-            ['Hint Content', '<div class="hint-block">Hint Content</div>'],
-            [false, ''],
-            [null, '<div class="hint-block">Hint for attributeName attribute</div>'],
-        ];
-    }
-
     /**
-     * @dataProvider hintDataProvider
-     *
-     * @param mixed $hint The hint content.
-     * @param string $expectedHtml The expected HTML.
+     * @dataProvider \yiiunit\framework\widgets\providers\ActiveFieldProvider::hintDataProvider
      */
-    public function testHint(mixed $hint, string $expectedHtml): void
+    public function testHint(bool|string|null $hint, string $expectedHtml): void
     {
         $this->activeField->hint($hint);
 
-        $this->assertSame($expectedHtml, $this->activeField->parts['{hint}']);
+        $this->assertSame(
+            $expectedHtml,
+            $this->activeField->parts['{hint}'],
+            'Rendered HTML does not match expected output',
+        );
     }
 
     public function testInput(): void
@@ -495,6 +520,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <input type="password" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
 
         // with options
@@ -505,6 +531,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <input type="password" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]" weird="value">
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -517,6 +544,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <input type="text" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -529,12 +557,18 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <input type="hidden" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testListBox(): void
     {
-        $this->activeField->listBox(['1' => 'Item One', '2' => 'Item 2']);
+        $this->activeField->listBox(
+            [
+                '1' => 'Item One',
+                '2' => 'Item 2',
+            ],
+        );
 
         $this->assertEqualsWithoutLE(
             <<<HTML
@@ -544,13 +578,22 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </select>
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
 
         // https://github.com/yiisoft/yii2/issues/8848
-        $this->activeField->listBox(['value1' => 'Item One', 'value2' => 'Item 2'], ['options' => [
-            'value1' => ['disabled' => true],
-            'value2' => ['label' => 'value 2'],
-        ]]);
+        $this->activeField->listBox(
+            [
+                'value1' => 'Item One',
+                'value2' => 'Item 2',
+            ],
+            [
+                'options' => [
+                    'value1' => ['disabled' => true],
+                    'value2' => ['label' => 'value 2'],
+                ],
+            ],
+        );
 
         $this->assertEqualsWithoutLE(
             <<<HTML
@@ -560,13 +603,23 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </select>
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->model->{$this->attributeName} = 'value2';
-        $this->activeField->listBox(['value1' => 'Item One', 'value2' => 'Item 2'], ['options' => [
-            'value1' => ['disabled' => true],
-            'value2' => ['label' => 'value 2'],
-        ]]);
+
+        $this->activeField->listBox(
+            [
+                'value1' => 'Item One',
+                'value2' => 'Item 2',
+            ],
+            [
+                'options' => [
+                    'value1' => ['disabled' => true],
+                    'value2' => ['label' => 'value 2'],
+                ],
+            ]
+        );
 
         $this->assertEqualsWithoutLE(
             <<<HTML
@@ -576,6 +629,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </select>
             HTML,
             $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -593,6 +647,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -601,23 +656,23 @@ class ActiveFieldTest extends \yiiunit\TestCase
         // setup: we want the real deal here!
         $this->activeField->setClientOptionsEmpty(false);
 
-        // expected empty
-        $actualValue = $this->activeField->getClientOptions();
-
-        $this->assertEmpty($actualValue);
+        $this->assertEmpty(
+            $this->activeField->getClientOptions(),
+            "'getClientOptions()' method should return an empty array.",
+        );
     }
 
     public function testGetClientOptionsWithActiveAttributeInScenario(): void
     {
         $this->activeField->setClientOptionsEmpty(false);
-
         $this->activeField->model->addRule($this->attributeName, 'yiiunit\framework\widgets\TestValidator');
-        $this->activeField->form->enableClientValidation = false;
 
-        // expected empty
-        $actualValue = $this->activeField->getClientOptions();
+        $this->activeField->form->enableClientValidation = false;
 
-        $this->assertEmpty($actualValue);
+        $this->assertEmpty(
+            $this->activeField->getClientOptions(),
+            "'getClientOptions()' method should return an empty array.",
+        );
     }
 
     public function testGetClientOptionsClientValidation(): void
@@ -625,16 +680,36 @@ class ActiveFieldTest extends \yiiunit\TestCase
         $this->activeField->setClientOptionsEmpty(false);
 
         $this->activeField->model->addRule($this->attributeName, 'yiiunit\framework\widgets\TestValidator');
+
         $this->activeField->enableClientValidation = true;
+
         $actualValue = $this->activeField->getClientOptions();
-        $expectedJsExpression = 'function (attribute, value, messages, deferred, $form) {return true;}';
-        $this->assertEquals($expectedJsExpression, $actualValue['validate']);
 
-        $this->assertNotTrue(isset($actualValue['validateOnChange']));
-        $this->assertNotTrue(isset($actualValue['validateOnBlur']));
-        $this->assertNotTrue(isset($actualValue['validateOnType']));
-        $this->assertNotTrue(isset($actualValue['validationDelay']));
-        $this->assertNotTrue(isset($actualValue['enableAjaxValidation']));
+        $this->assertEquals(
+            'function (attribute, value, messages, deferred, $form) {return true;}',
+            $actualValue['validate'],
+            'Client validation function is not as expected.',
+        );
+        $this->assertNotTrue(
+            isset($actualValue['validateOnChange']),
+            "Should not be set by default",
+        );
+        $this->assertNotTrue(
+            isset($actualValue['validateOnBlur']),
+            "Should not be set by default",
+        );
+        $this->assertNotTrue(
+            isset($actualValue['validateOnType']),
+            "Should not be set by default",
+        );
+        $this->assertNotTrue(
+            isset($actualValue['validationDelay']),
+            "Should not be set by default",
+        );
+        $this->assertNotTrue(
+            isset($actualValue['enableAjaxValidation']),
+            "Should not be set by default",
+        );
 
         $this->activeField->validateOnChange = $expectedValidateOnChange = false;
         $this->activeField->validateOnBlur = $expectedValidateOnBlur = false;
@@ -644,17 +719,39 @@ class ActiveFieldTest extends \yiiunit\TestCase
 
         $actualValue = $this->activeField->getClientOptions();
 
-        $this->assertSame($expectedValidateOnChange, $actualValue['validateOnChange']);
-        $this->assertSame($expectedValidateOnBlur, $actualValue['validateOnBlur']);
-        $this->assertSame($expectedValidateOnType, $actualValue['validateOnType']);
-        $this->assertSame($expectedValidationDelay, $actualValue['validationDelay']);
-        $this->assertSame($expectedEnableAjaxValidation, $actualValue['enableAjaxValidation']);
+        $this->assertSame(
+            $expectedValidateOnChange,
+            $actualValue['validateOnChange'],
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $expectedValidateOnBlur,
+            $actualValue['validateOnBlur'],
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $expectedValidateOnType,
+            $actualValue['validateOnType'],
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $expectedValidationDelay,
+            $actualValue['validationDelay'],
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $expectedEnableAjaxValidation,
+            $actualValue['enableAjaxValidation'],
+            'Should be the same as the set value.',
+        );
     }
 
     public function testGetClientOptionsValidatorWhenClientSet(): void
     {
         $this->activeField->setClientOptionsEmpty(false);
+
         $this->activeField->enableAjaxValidation = true;
+
         $this->activeField->model->addRule($this->attributeName, 'yiiunit\framework\widgets\TestValidator');
 
         foreach ($this->activeField->model->validators as $validator) {
@@ -662,10 +759,15 @@ class ActiveFieldTest extends \yiiunit\TestCase
         }
 
         $actualValue = $this->activeField->getClientOptions();
+
         $expectedJsExpression = 'function (attribute, value, messages, deferred, $form) {if ((function (attribute, value) '
             . "{ return 'yii2' == 'yii2'; })(attribute, value)) { return true; }}";
 
-        $this->assertSame($expectedJsExpression, $actualValue['validate']->expression);
+        $this->assertSame(
+            $expectedJsExpression,
+            $actualValue['validate']->expression,
+            'Client validation function is not as expected.',
+        );
     }
 
     /**
@@ -675,7 +777,11 @@ class ActiveFieldTest extends \yiiunit\TestCase
     {
         $this->activeField->fileInput();
 
-        $this->assertSame('multipart/form-data', $this->activeField->form->options['enctype']);
+        $this->assertSame(
+            'multipart/form-data',
+            $this->activeField->form->options['enctype'],
+            'Should be the same as the set value.',
+        );
     }
 
     /**
@@ -684,28 +790,35 @@ class ActiveFieldTest extends \yiiunit\TestCase
     public function testGetClientOptionsWithCustomInputId(): void
     {
         $this->activeField->setClientOptionsEmpty(false);
-
         $this->activeField->model->addRule($this->attributeName, 'yiiunit\framework\widgets\TestValidator');
+
         $this->activeField->inputOptions['id'] = 'custom-input-id';
+
         $this->activeField->textInput();
-        $actualValue = $this->activeField->getClientOptions();
 
-        $this->assertArraySubset([
-            'id' => 'custom-input-id',
-            'name' => $this->attributeName,
-            'container' => '.field-custom-input-id',
-            'input' => '#custom-input-id',
-        ], $actualValue);
+        $this->assertArraySubset(
+            [
+                'id' => 'custom-input-id',
+                'name' => $this->attributeName,
+                'container' => '.field-custom-input-id',
+                'input' => '#custom-input-id',
+            ],
+            $this->activeField->getClientOptions(),
+            message: "'getClientOptions()' method should return correct options array.",
+        );
 
         $this->activeField->textInput(['id' => 'custom-textinput-id']);
-        $actualValue = $this->activeField->getClientOptions();
 
-        $this->assertArraySubset([
-            'id' => 'custom-textinput-id',
-            'name' => $this->attributeName,
-            'container' => '.field-custom-textinput-id',
-            'input' => '#custom-textinput-id',
-        ], $actualValue);
+        $this->assertArraySubset(
+            [
+                'id' => 'custom-textinput-id',
+                'name' => $this->attributeName,
+                'container' => '.field-custom-textinput-id',
+                'input' => '#custom-textinput-id',
+            ],
+            $this->activeField->getClientOptions(),
+            message: "'getClientOptions()' method should return correct options array.",
+        );
     }
 
     public function testAriaAttributes(): void
@@ -722,12 +835,14 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testAriaRequiredAttribute(): void
     {
         $this->activeField->addAriaAttributes = true;
+
         $this->helperModel->addRule([$this->attributeName], 'required');
 
         $this->assertEqualsWithoutLE(
@@ -740,12 +855,14 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testAriaInvalidAttribute(): void
     {
         $this->activeField->addAriaAttributes = true;
+
         $this->helperModel->addError($this->attributeName, 'Some error');
 
         $this->assertEqualsWithoutLE(
@@ -758,12 +875,13 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testTabularAriaAttributes(): void
     {
-        $this->activeField->attribute = '[0]' . $this->attributeName;
+        $this->activeField->attribute = "[0]{$this->attributeName}";
         $this->activeField->addAriaAttributes = true;
 
         $this->assertEqualsWithoutLE(
@@ -776,13 +894,15 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testTabularAriaRequiredAttribute(): void
     {
-        $this->activeField->attribute = '[0]' . $this->attributeName;
+        $this->activeField->attribute = "[0]{$this->attributeName}";
         $this->activeField->addAriaAttributes = true;
+
         $this->helperModel->addRule([$this->attributeName], 'required');
 
         $this->assertEqualsWithoutLE(
@@ -795,13 +915,15 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testTabularAriaInvalidAttribute(): void
     {
-        $this->activeField->attribute = '[0]' . $this->attributeName;
+        $this->activeField->attribute = "[0]{$this->attributeName}";
         $this->activeField->addAriaAttributes = true;
+
         $this->helperModel->addError($this->attributeName, 'Some error');
 
         $this->assertEqualsWithoutLE(
@@ -814,6 +936,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -826,47 +949,91 @@ class ActiveFieldTest extends \yiiunit\TestCase
             <input type="hidden" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
             HTML,
             trim($this->activeField->hiddenInput()->label(false)->error(false)->hint(false)->render()),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testWidget(): void
     {
         $this->activeField->widget(TestInputWidget::class);
-        $this->assertSame('Render: ' . TestInputWidget::class, $this->activeField->parts['{input}']);
+
+        $this->assertSame(
+            'Render: ' . TestInputWidget::class,
+            $this->activeField->parts['{input}'],
+            'Rendered HTML does not match expected output',
+        );
+
         $widget = TestInputWidget::$lastInstance;
 
-        $this->assertSame($this->activeField->model, $widget->model);
-        $this->assertSame($this->activeField->attribute, $widget->attribute);
-        $this->assertSame($this->activeField->form->view, $widget->view);
-        $this->assertSame($this->activeField, $widget->field);
+        $this->assertSame(
+            $this->activeField->model,
+            $widget->model,
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $this->activeField->attribute,
+            $widget->attribute,
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $this->activeField->form->view,
+            $widget->view,
+            'Should be the same as the set value.',
+        );
+        $this->assertSame(
+            $this->activeField,
+            $widget->field,
+            'Should be the same as the set value.',
+        );
 
         $this->activeField->widget(TestInputWidget::class, ['options' => ['id' => 'test-id']]);
-        $this->assertSame('test-id', $this->activeField->labelOptions['for']);
+
+        $this->assertSame(
+            'test-id',
+            $this->activeField->labelOptions['for'],
+            'Should be the same as the set value.',
+        );
     }
 
     public function testWidgetOptions(): void
     {
         $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
         $this->activeField->model->addError('attributeName', 'error');
 
         $this->activeField->widget(TestInputWidget::class);
+
         $widget = TestInputWidget::$lastInstance;
+
         $expectedOptions = [
             'class' => 'form-control has-error',
             'aria-invalid' => 'true',
             'id' => 'activefieldtestmodel-attributename',
         ];
-        $this->assertSame($expectedOptions, $widget->options);
+
+        $this->assertSame(
+            $expectedOptions,
+            $widget->options,
+            'Should be the same as the set value.',
+        );
 
         $this->activeField->inputOptions = [];
+
         $this->activeField->widget(TestInputWidget::class);
+
         $widget = TestInputWidget::$lastInstance;
+
         $expectedOptions = [
             'class' => 'has-error',
             'aria-invalid' => 'true',
             'id' => 'activefieldtestmodel-attributename',
         ];
-        $this->assertSame($expectedOptions, $widget->options);
+
+        $this->assertSame(
+            $expectedOptions,
+            $widget->options,
+            'Should be the same as the set value.',
+        );
     }
 
     /**
@@ -881,13 +1048,11 @@ class ActiveFieldTest extends \yiiunit\TestCase
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="test-wrapper field-activefieldtestmodel-attributename">
-
             <input type="hidden" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
-
-
             </div>
             HTML,
-            trim($this->activeField->hiddenInput()->label(false)->error(false)->hint(false)->render()),
+            $this->normalizeHTML($this->activeField->hiddenInput()->label(false)->error(false)->hint(false)->render()),
+            'Rendered HTML does not match expected output',
         );
 
         $this->activeField->options = ['class' => ['test-wrapper', 'test-add']];
@@ -895,45 +1060,75 @@ class ActiveFieldTest extends \yiiunit\TestCase
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="test-wrapper test-add field-activefieldtestmodel-attributename">
-
             <input type="hidden" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
-
-
             </div>
             HTML,
-            trim($this->activeField->hiddenInput()->label(false)->error(false)->hint(false)->render()),
+            $this->normalizeHTML($this->activeField->hiddenInput()->label(false)->error(false)->hint(false)->render()),
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testInputOptionsTransferToWidget(): void
     {
-        $widget = $this->activeField->widget(TestMaskedInput::class, [
-            'mask' => '999-999-9999',
-            'options' => ['placeholder' => 'pholder_direct'],
-        ]);
-        $this->assertStringContainsString('placeholder="pholder_direct"', (string) $widget);
+        $widget = $this->activeField->widget(
+            TestMaskedInput::class,
+            [
+                'mask' => '999-999-9999',
+                'options' => ['placeholder' => 'pholder_direct'],
+            ],
+        );
+
+        $this->assertStringContainsString(
+            'placeholder="pholder_direct"',
+            (string) $widget,
+            'Should be the same as the set value.',
+        );
 
         // use regex clientOptions instead mask
-        $widget = $this->activeField->widget(TestMaskedInput::class, [
-            'options' => ['placeholder' => 'pholder_direct'],
-            'clientOptions' => ['regex' => '^.*$'],
-        ]);
-        $this->assertStringContainsString('placeholder="pholder_direct"', (string) $widget);
+        $widget = $this->activeField->widget(
+            TestMaskedInput::class,
+            [
+                'options' => ['placeholder' => 'pholder_direct'],
+                'clientOptions' => ['regex' => '^.*$'],
+            ],
+        );
+
+        $this->assertStringContainsString(
+            'placeholder="pholder_direct"',
+            (string) $widget,
+            'Should be the same as the set value.',
+        );
 
         // transfer options from ActiveField to widget
         $this->activeField->inputOptions = ['placeholder' => 'pholder_input'];
-        $widget = $this->activeField->widget(TestMaskedInput::class, [
-            'mask' => '999-999-9999',
-        ]);
-        $this->assertStringContainsString('placeholder="pholder_input"', (string) $widget);
+
+        $widget = $this->activeField->widget(
+            TestMaskedInput::class,
+            ['mask' => '999-999-9999'],
+        );
+
+        $this->assertStringContainsString(
+            'placeholder="pholder_input"',
+            (string) $widget,
+            'Should be the same as the set value.',
+        );
 
         // set both AF and widget options (second one takes precedence)
         $this->activeField->inputOptions = ['placeholder' => 'pholder_both_input'];
-        $widget = $this->activeField->widget(TestMaskedInput::class, [
-            'mask' => '999-999-9999',
-            'options' => ['placeholder' => 'pholder_both_direct']
-        ]);
-        $this->assertStringContainsString('placeholder="pholder_both_direct"', (string) $widget);
+
+        $widget = $this->activeField->widget(
+            TestMaskedInput::class,
+            [
+                'mask' => '999-999-9999',
+                'options' => ['placeholder' => 'pholder_both_direct']
+            ],
+        );
+
+        $this->assertStringContainsString(
+            'placeholder="pholder_both_direct"',
+            (string) $widget,
+            'Should be the same as the set value.',
+        );
     }
 
     public function testRadioEnclosedByLabelFalseWithLabelOptions(): void
@@ -959,30 +1154,24 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that radio renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testRadioEnclosedByLabelFalseWithLabelOptionsAndLabelFalse(): void
     {
-        $this->activeField->radio(
-            [
-                'label' => false,
-            ],
-            false,
-        );
+        $this->activeField->radio(['label' => false], false);
 
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="form-group field-activefieldtestmodel-attributename">
-
             <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
             <div class="hint-block">Hint for attributeName attribute</div>
             <div class="help-block"></div>
             </div>
             HTML,
-            $this->activeField->render(),
-            'Failed asserting that radio renders correctly.',
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -1010,7 +1199,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that radio renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -1036,18 +1225,13 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that radio renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testRadioEnclosedByLabelFalseWithoutLabelOptions(): void
     {
-        $this->activeField->radio(
-            [
-                'label' => 'Select Option A',
-            ],
-            false,
-        );
+        $this->activeField->radio(['label' => 'Select Option A'], false);
 
         $this->assertEqualsWithoutLE(
             <<<HTML
@@ -1059,7 +1243,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that radio renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -1086,30 +1270,24 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that checkbox renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testCheckboxEnclosedByLabelFalseWithLabelOptionsAndLabelFalse(): void
     {
-        $this->activeField->checkbox(
-            [
-                'label' => false,
-            ],
-            false,
-        );
+        $this->activeField->checkbox(['label' => false], false);
 
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="form-group field-activefieldtestmodel-attributename">
-
             <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
             <div class="hint-block">Hint for attributeName attribute</div>
             <div class="help-block"></div>
             </div>
             HTML,
-            $this->activeField->render(),
-            'Failed asserting that checkbox renders correctly.',
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -1137,7 +1315,7 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that checkbox renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
@@ -1163,18 +1341,13 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that checkbox renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
     public function testCheckboxEnclosedByLabelFalseWithoutLabelOptions(): void
     {
-        $this->activeField->checkbox(
-            [
-                'label' => 'Custom Label',
-            ],
-            false,
-        );
+        $this->activeField->checkbox(['label' => 'Custom Label'], false);
 
         $this->assertEqualsWithoutLE(
             <<<HTML
@@ -1186,20 +1359,821 @@ class ActiveFieldTest extends \yiiunit\TestCase
             </div>
             HTML,
             $this->activeField->render(),
-            'Failed asserting that checkbox renders correctly.',
+            'Rendered HTML does not match expected output',
         );
     }
 
-    /**
-     * Helper methods.
-     */
-    protected function getView()
+    public function testInputWithValidationStateOnInput(): void
     {
-        $view = new View();
-        $view->setAssetManager(new AssetManager([
-            'basePath' => '@testWebRoot/assets',
-            'baseUrl' => '@testWeb/assets',
-        ]));
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'Input validation error');
+        $this->activeField->input('number');
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="number" id="activefieldtestmodel-attributename" class="form-control has-error" name="ActiveFieldTestModel[attributeName]" aria-invalid="true">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Input validation error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testPasswordInput(): void
+    {
+        $this->activeField->passwordInput();
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="password" id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testPasswordInputWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+        $this->activeField->model->addError($this->attributeName, 'Password error');
+
+        $this->activeField->passwordInput();
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="password" id="activefieldtestmodel-attributename" class="form-control has-error" name="ActiveFieldTestModel[attributeName]" aria-invalid="true">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Password error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testFileInputWithCustomInputOptions(): void
+    {
+        $this->activeField->inputOptions = ['class' => 'custom-file-input', 'data-test' => 'file-upload'];
+
+        $this->activeField->fileInput(['accept' => 'image/*', 'id' => 'custom-file-id']);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-custom-file-id">
+            <label class="control-label" for="custom-file-id">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><input type="file" id="custom-file-id" class="custom-file-input" name="ActiveFieldTestModel[attributeName]" data-test="file-upload" accept="image/*">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testFileInputWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'File upload error');
+
+        $this->activeField->fileInput();
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><input type="file" id="activefieldtestmodel-attributename" class="has-error" name="ActiveFieldTestModel[attributeName]" aria-invalid="true">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">File upload error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testTextarea(): void
+    {
+        $this->activeField->textarea();
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <textarea id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]"></textarea>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testTextareaWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+        $this->activeField->model->addError($this->attributeName, 'Some error');
+
+        $this->activeField->textarea();
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <textarea id="activefieldtestmodel-attributename" class="form-control has-error" name="ActiveFieldTestModel[attributeName]" aria-invalid="true"></textarea>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Some error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioEnclosedByLabelFalse(): void
+    {
+        $this->activeField->radio([], false);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioEnclosedByLabelFalseWithCustomLabel(): void
+    {
+        $this->activeField->radio(
+            [
+                'label' => 'Select Option A',
+                'labelOptions' => [
+                    'class' => 'custom-radio-label',
+                    'data-option' => 'option-a',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="custom-radio-label" data-option="option-a" for="activefieldtestmodel-attributename">Select Option A</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioEnclosedByLabelFalseWithCustomLabelFalse(): void
+    {
+        $this->activeField->radio(
+            [
+                'label' => false,
+                'labelOptions' => [
+                    'class' => 'custom-radio-label',
+                    'data-option' => 'option-a',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+
+    public function testRadioEnclosedByLabelFalseWithCustomLabelTag(): void
+    {
+        $this->activeField->radio(
+            [
+                'label' => 'Choose This Option',
+                'labelOptions' => [
+                    'class' => 'radio-option-label',
+                    'data-value' => 'choice-1',
+                    'tag' => 'span',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <span class="radio-option-label" data-value="choice-1">Choose This Option</span>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioEnclosedByLabelFalseWithCustomLabelTagFalse(): void
+    {
+        $this->activeField->radio(
+            [
+                'label' => '<div class="radio-custom-wrapper"><strong>Premium Option</strong> <em>(Recommended)</em></div>',
+                'labelOptions' => ['tag' => false],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <div class="radio-custom-wrapper"><strong>Premium Option</strong> <em>(Recommended)</em></div>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioEnclosedByLabelTrue(): void
+    {
+        $this->activeField->radio([], true);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><label><input type="radio" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1"> Attribute Name</label>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelFalse(): void
+    {
+        $this->activeField->checkbox([], false);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelFalseWithCustomLabel(): void
+    {
+        $this->activeField->checkbox(
+            [
+                'label' => 'Custom Label',
+                'labelOptions' => [
+                    'class' => 'custom-label-class',
+                    'data-test' => 'custom-label-data',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="custom-label-class" data-test="custom-label-data" for="activefieldtestmodel-attributename">Custom Label</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelFalseWithCustomLabelFalse(): void
+    {
+        $this->activeField->checkbox(
+            [
+                'label' => false,
+                'labelOptions' => [
+                    'class' => 'custom-label-class',
+                    'data-test' => 'custom-label-data',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelFalseWithCustomLabelTag(): void
+    {
+        $this->activeField->checkbox(
+            [
+                'label' => 'Custom Label',
+                'labelOptions' => [
+                    'class' => 'custom-label-class',
+                    'data-test' => 'custom-label-data',
+                    'tag' => 'span',
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <span class="custom-label-class" data-test="custom-label-data">Custom Label</span>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelFalseWithCustomLabelTagFalse(): void
+    {
+        $this->activeField->checkbox(
+            [
+                'label' => 'Custom Label',
+                'labelOptions' => [
+                    'tag' => false,
+                ],
+            ],
+            false,
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            Custom Label
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1">
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxEnclosedByLabelTrue(): void
+    {
+        $this->activeField->checkbox([], true);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value="0"><label><input type="checkbox" id="activefieldtestmodel-attributename" name="ActiveFieldTestModel[attributeName]" value="1"> Attribute Name</label>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->normalizeHTML($this->activeField->render()),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testDropDownList(): void
+    {
+        $this->activeField->dropDownList(
+            [
+                '1' => 'Item One',
+                '2' => 'Item Two',
+            ],
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <select id="activefieldtestmodel-attributename" class="form-control" name="ActiveFieldTestModel[attributeName]">
+            <option value="1">Item One</option>
+            <option value="2">Item Two</option>
+            </select>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testDropDownListWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'Some error');
+        $this->activeField->dropDownList(['1' => 'Item One']);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <select id="activefieldtestmodel-attributename" class="form-control has-error" name="ActiveFieldTestModel[attributeName]" aria-invalid="true">
+            <option value="1">Item One</option>
+            </select>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Some error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testListboxWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'Some error');
+        $this->activeField->listBox(
+            [
+                '1' => 'Item One',
+                '2' => 'Item 2',
+            ],
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label" for="activefieldtestmodel-attributename">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><select id="activefieldtestmodel-attributename" class="form-control has-error" name="ActiveFieldTestModel[attributeName]" size="4" aria-invalid="true">
+            <option value="1">Item One</option>
+            <option value="2">Item 2</option>
+            </select>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Some error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxList(): void
+    {
+        $this->activeField->checkboxList(
+            [
+                '1' => 'Item One',
+                '2' => 'Item Two',
+            ],
+        );
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><div id="activefieldtestmodel-attributename"><label><input type="checkbox" name="ActiveFieldTestModel[attributeName][]" value="1"> Item One</label>
+            <label><input type="checkbox" name="ActiveFieldTestModel[attributeName][]" value="2"> Item Two</label></div>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block"></div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testCheckboxListWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'Some error');
+        $this->activeField->checkboxList(['1' => 'Item One']);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><div id="activefieldtestmodel-attributename" class="has-error" aria-invalid="true"><label><input type="checkbox" name="ActiveFieldTestModel[attributeName][]" value="1"> Item One</label></div>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Some error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testRadioListWithValidationStateOnInput(): void
+    {
+        $this->activeField->form->validationStateOn = ActiveForm::VALIDATION_STATE_ON_INPUT;
+
+        $this->activeField->model->addError($this->attributeName, 'Some error');
+        $this->activeField->radioList(['1' => 'Item One']);
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <div class="form-group field-activefieldtestmodel-attributename">
+            <label class="control-label">Attribute Name</label>
+            <input type="hidden" name="ActiveFieldTestModel[attributeName]" value=""><div id="activefieldtestmodel-attributename" class="has-error" role="radiogroup" aria-invalid="true"><label><input type="radio" name="ActiveFieldTestModel[attributeName]" value="1"> Item One</label></div>
+            <div class="hint-block">Hint for attributeName attribute</div>
+            <div class="help-block">Some error</div>
+            </div>
+            HTML,
+            $this->activeField->render(),
+            'Rendered HTML does not match expected output',
+        );
+    }
+
+    public function testGetClientOptionsWithAriaAttributesFalse(): void
+    {
+        $_SERVER['REQUEST_URI'] = 'https://example.com/';
+
+        Yii::$app->assetManager->hashCallback = static fn ($path): string => '5a1b552';
+
+        $model = new DynamicModel(['name']);
+
+        $model->addRule(['name'], 'required');
+
+        $view = Yii::$app->getView();
+
+        ob_start();
+        ob_implicit_flush(false);
+        $form = ActiveForm::begin(
+            [
+                'id' => 'w0',
+                'view' => $view,
+            ],
+        );
+        $field = $form->field($model, 'name');
+        $field->addAriaAttributes = false;
+        echo $field;
+        $form::end();
+        $expectedForm = ob_get_clean();
+
+        $csrfToken = Yii::$app->request->csrfToken;
+        $validate = '"validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Name cannot be blank."}';
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <!DOCTYPE html>
+            <html>
+            <head>
+                <title>Test</title>
+                </head>
+            <body>
+            <form id="w0" action="/" method="post">
+            <input type="hidden" name="_csrf" value="$csrfToken"><div class="form-group field-dynamicmodel-name required">
+            <label class="control-label" for="dynamicmodel-name">Name</label>
+            <input type="text" id="dynamicmodel-name" class="form-control" name="DynamicModel[name]">
+            <div class="help-block"></div>
+            </div></form>
+            <script src="/assets/5a1b552/jquery.js"></script>
+            <script src="/assets/5a1b552/yii.js"></script>
+            <script src="/assets/5a1b552/yii.validation.js"></script>
+            <script src="/assets/5a1b552/yii.activeForm.js"></script>
+            <script>jQuery(function ($) {
+            jQuery('#w0').yiiActiveForm([{"id":"dynamicmodel-name","name":"name","container":".field-dynamicmodel-name","input":"#dynamicmodel-name",$validate);},"updateAriaInvalid":false}], []);
+            });</script></body>
+            </html>
+            HTML,
+            $this->normalizeHTML($view->render('@yiiunit/data/views/layout.php', ['content' => $expectedForm])),
+            'Failed asserting that the generated form matches the expected view.',
+        );
+    }
+
+    public function testGetClientOptionsWithCustomErrorSelector(): void
+    {
+        $_SERVER['REQUEST_URI'] = 'https://example.com/';
+
+        Yii::$app->assetManager->hashCallback = static fn ($path): string => '5a1b552';
+
+        $model = new DynamicModel(['name']);
+
+        $model->addRule(['name'], 'required');
+
+        $view = Yii::$app->getView();
+
+        ob_start();
+        ob_implicit_flush(false);
+        $form = ActiveForm::begin(
+            [
+                'id' => 'w0',
+                'view' => $view,
+            ],
+        );
+        $field = $form->field($model, 'name');
+        $field->selectors = ['error' => '.custom-error-selector'];
+        echo $field;
+        $form::end();
+        $expectedForm = ob_get_clean();
+
+        $csrfToken = Yii::$app->request->csrfToken;
+        $validate = '"validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Name cannot be blank."});}';
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <!DOCTYPE html>
+            <html>
+            <head>
+                <title>Test</title>
+                </head>
+            <body>
+            <form id="w0" action="/" method="post">
+            <input type="hidden" name="_csrf" value="$csrfToken"><div class="form-group field-dynamicmodel-name required">
+            <label class="control-label" for="dynamicmodel-name">Name</label>
+            <input type="text" id="dynamicmodel-name" class="form-control" name="DynamicModel[name]" aria-required="true">
+            <div class="help-block"></div>
+            </div></form>
+            <script src="/assets/5a1b552/jquery.js"></script>
+            <script src="/assets/5a1b552/yii.js"></script>
+            <script src="/assets/5a1b552/yii.validation.js"></script>
+            <script src="/assets/5a1b552/yii.activeForm.js"></script>
+            <script>jQuery(function ($) {
+            jQuery('#w0').yiiActiveForm([{"id":"dynamicmodel-name","name":"name","container":".field-dynamicmodel-name","input":"#dynamicmodel-name","error":".custom-error-selector",$validate}], []);
+            });</script></body>
+            </html>
+            HTML,
+            $this->normalizeHTML($view->render('@yiiunit/data/views/layout.php', ['content' => $expectedForm])),
+            'Failed asserting that the generated form matches the expected view.',
+        );
+    }
+
+    public function testGetClientOptionsWithErrorDefaultTag(): void
+    {
+        $_SERVER['REQUEST_URI'] = 'https://example.com/';
+
+        Yii::$app->assetManager->hashCallback = static fn ($path): string => '5a1b552';
+
+        $model = new DynamicModel(['name']);
+
+        $model->addRule(['name'], 'string');
+
+        $view = Yii::$app->getView();
+
+        ob_start();
+        ob_implicit_flush(false);
+        $form = ActiveForm::begin([
+            'id' => 'w0',
+            'view' => $view,
+        ]);
+        $field = $form->field($model, 'name');
+        unset($field->selectors['error']);
+        $field->errorOptions = [];
+        echo $field;
+        $form::end();
+        $expectedForm = ob_get_clean();
+
+        $csrfToken = Yii::$app->request->csrfToken;
+        $validate = '"validate":function (attribute, value, messages, deferred, $form) {yii.validation.string(value, messages, {"message":"Name must be a string.","skipOnEmpty":1';
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <!DOCTYPE html>
+            <html>
+            <head>
+                <title>Test</title>
+                </head>
+            <body>
+            <form id="w0" action="/" method="post">
+            <input type="hidden" name="_csrf" value="$csrfToken"><div class="form-group field-dynamicmodel-name">
+            <label class="control-label" for="dynamicmodel-name">Name</label>
+            <input type="text" id="dynamicmodel-name" class="form-control" name="DynamicModel[name]">
+            <div></div>
+            </div></form>
+            <script src="/assets/5a1b552/jquery.js"></script>
+            <script src="/assets/5a1b552/yii.js"></script>
+            <script src="/assets/5a1b552/yii.validation.js"></script>
+            <script src="/assets/5a1b552/yii.activeForm.js"></script>
+            <script>jQuery(function ($) {
+            jQuery('#w0').yiiActiveForm([{"id":"dynamicmodel-name","name":"name","container":".field-dynamicmodel-name","input":"#dynamicmodel-name","error":"span",$validate});}}], []);
+            });</script></body>
+            </html>
+            HTML,
+            $this->normalizeHTML($view->render('@yiiunit/data/views/layout.php', ['content' => $expectedForm])),
+            'Failed asserting that the generated form matches the expected view.',
+        );
+    }
+
+    public function testGetClientOptionsWithErrorOptionsClass(): void
+    {
+        $_SERVER['REQUEST_URI'] = 'https://example.com/';
+
+        Yii::$app->assetManager->hashCallback = static fn ($path): string => '5a1b552';
+
+        $model = new DynamicModel(['name']);
+
+        $model->addRule(['name'], 'required');
+
+        $view = Yii::$app->getView();
+
+        ob_start();
+        ob_implicit_flush(false);
+        $form = ActiveForm::begin(
+            [
+                'id' => 'w0',
+                'view' => $view,
+            ],
+        );
+        $field = $form->field($model, 'name');
+        unset($field->selectors['error']);
+        $field->errorOptions = ['class' => 'error-class another-class'];
+        echo $field;
+        $form::end();
+        $expectedForm = ob_get_clean();
+
+        $csrfToken = Yii::$app->request->csrfToken;
+        $validate = '"validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Name cannot be blank."});}';
+
+        $this->assertEqualsWithoutLE(
+            <<<HTML
+            <!DOCTYPE html>
+            <html>
+            <head>
+                <title>Test</title>
+                </head>
+            <body>
+            <form id="w0" action="/" method="post">
+            <input type="hidden" name="_csrf" value="$csrfToken"><div class="form-group field-dynamicmodel-name required">
+            <label class="control-label" for="dynamicmodel-name">Name</label>
+            <input type="text" id="dynamicmodel-name" class="form-control" name="DynamicModel[name]" aria-required="true">
+            <div class="error-class another-class"></div>
+            </div></form>
+            <script src="/assets/5a1b552/jquery.js"></script>
+            <script src="/assets/5a1b552/yii.js"></script>
+            <script src="/assets/5a1b552/yii.validation.js"></script>
+            <script src="/assets/5a1b552/yii.activeForm.js"></script>
+            <script>jQuery(function ($) {
+            jQuery('#w0').yiiActiveForm([{"id":"dynamicmodel-name","name":"name","container":".field-dynamicmodel-name","input":"#dynamicmodel-name","error":".error-class.another-class",$validate}], []);
+            });</script></body>
+            </html>
+            HTML,
+            $this->normalizeHTML($view->render('@yiiunit/data/views/layout.php', ['content' => $expectedForm])),
+            'Failed asserting that the generated form matches the expected view.',
+        );
+    }
+
+    /**
+     * Helper methods.
+     */
+    protected function getView()
+    {
+        $view = new View();
+
+        $view->setAssetManager(
+            new AssetManager(
+                [
+                    'basePath' => '@testWebRoot/assets',
+                    'baseUrl' => '@testWeb/assets',
+                ],
+            ),
+        );
 
         return $view;
     }
diff --git a/tests/framework/widgets/ActiveFormTest.php b/tests/framework/widgets/ActiveFormTest.php
index c1182c909..c5729d9da 100644
--- a/tests/framework/widgets/ActiveFormTest.php
+++ b/tests/framework/widgets/ActiveFormTest.php
@@ -202,33 +202,28 @@ final class ActiveFormTest extends \yiiunit\TestCase
             <div class="form-group field-dynamicmodel-name">
             <label class="control-label" for="dynamicmodel-name">Name</label>
             <input type="text" id="dynamicmodel-name" class="form-control has-error" name="DynamicModel[name]" aria-invalid="true">
-
             <div class="help-block">I have an error!</div>
             </div>
             HTML,
-            (string) $form->field($model, 'name'),
+            $this->normalizeHTML((string) $form->field($model, 'name')),
         );
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="form-group field-dynamicmodel-name">
-
             <input type="hidden" name="DynamicModel[name]" value="0"><label><input type="checkbox" id="dynamicmodel-name" class="has-error" name="DynamicModel[name]" value="1" aria-invalid="true"> Name</label>
-
             <div class="help-block">I have an error!</div>
             </div>
             HTML,
-            (string) $form->field($model, 'name')->checkbox(),
+            $this->normalizeHTML((string) $form->field($model, 'name')->checkbox()),
         );
         $this->assertEqualsWithoutLE(
             <<<HTML
             <div class="form-group field-dynamicmodel-name">
-
             <input type="hidden" name="DynamicModel[name]" value="0"><label><input type="radio" id="dynamicmodel-name" class="has-error" name="DynamicModel[name]" value="1" aria-invalid="true"> Name</label>
-
             <div class="help-block">I have an error!</div>
             </div>
             HTML,
-            (string) $form->field($model, 'name')->radio(),
+            $this->normalizeHTML((string) $form->field($model, 'name')->radio()),
         );
     }
 
diff --git a/tests/framework/widgets/providers/ActiveFieldProvider.php b/tests/framework/widgets/providers/ActiveFieldProvider.php
new file mode 100644
index 000000000..1f5b67a06
--- /dev/null
+++ b/tests/framework/widgets/providers/ActiveFieldProvider.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @link https://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license https://www.yiiframework.com/license/
+ */
+
+declare(strict_types=1);
+
+namespace yiiunit\framework\widgets\providers;
+
+/**
+ * @author Wilmer Arambula <terabytesoftw@gmail.com>
+ *
+ * @since 2.2.0
+ */
+final class ActiveFieldProvider
+{
+    /**
+     * Provides test data for hint field rendering validation.
+     *
+     * This provider supplies test cases for validating hint content rendering in ActiveField widgets, including
+     * scenarios with false values, null values, and custom hint content.
+     *
+     * @return array test data with hint configurations and expected HTML output.
+     *
+     * @phpstan-return array<array{bool|string|null, string}>
+     */
+    public static function hintDataProvider(): array
+    {
+        return [
+            [
+                false,
+                '',
+            ],
+            [
+                null,
+                '<div class="hint-block">Hint for attributeName attribute</div>',
+            ],
+            [
+                'Hint Content',
+                '<div class="hint-block">Hint Content</div>',
+            ],
+        ];
+    }
+}