diff --git a/.bowerrc b/.bowerrc
index 1669168f2..a39b5b0fb 100644
--- a/.bowerrc
+++ b/.bowerrc
@@ -1,3 +1,3 @@
{
- "directory" : "vendor/bower"
+ "directory" : "vendor/bower-asset"
}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..011c056bb
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,8 @@
+# Reformat code te be PSR-2 compatible
+93cc82c4bf42cea403e1acaab201338bea304b6e
+# Convert to short syntax (array)
+332030325fbad38a64c5e60980d3b14b6434d6dd
+# Convert to short syntax
+b0fcdfab1aecaeb620f75a83f15aa02bc25765a0
+# Fix codestyle
+0f91f32ecff52ef479addb0a5013342d59fe0697
diff --git a/.gitattributes b/.gitattributes
index 3e9092c68..705fb391d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,7 @@
# Ignore all test and documentation for archive
-/.github export-ignore
-/.gitattributes export-ignore
-/.scrutinizer.yml export-ignore
-/.travis.yml export-ignore
-/docs export-ignore
+/.github export-ignore
+/.git-blame-ignore-revs export-ignore
+/.gitattributes export-ignore
+/.scrutinizer.yml export-ignore
+/.travis.yml export-ignore
+/docs export-ignore
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 3d8d32c25..ea75bda5f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -3,5 +3,5 @@ Contributing to Yii2
- [Report an issue](https://github.com/yiisoft/yii2/blob/master/docs/internals/report-an-issue.md)
- [Translate documentation or messages](https://github.com/yiisoft/yii2/blob/master/docs/internals/translation-workflow.md)
-- [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)
+- [Give us feedback or start a design discussion](https://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)
- [Contribute to the core code or fix bugs](https://github.com/yiisoft/yii2/blob/master/docs/internals/git-workflow.md)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..692239301
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+open_collective: yiisoft
+tidelift: "packagist/yiisoft/yii2-app-basic"
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 52ec8692f..a2ee92439 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -9,6 +9,6 @@
| Q | A
| ---------------- | ---
-| Yii vesion |
+| Yii version |
| PHP version |
| Operating system |
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 000000000..405acca4e
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,6 @@
+# Security Policy
+
+Please use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you find in Yii.
+DO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help.
+
+Please note that as a non-commercial OpenSource project we are not able to pay bounties at the moment.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..a5beac653
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,83 @@
+on:
+ - pull_request
+ - push
+
+name: build
+
+jobs:
+ tests:
+ name: PHP ${{ matrix.php }} - ${{ matrix.os }}
+
+ env:
+ extensions: dom, json, gd, imagick
+ key: cache-v4
+
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+
+ php:
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ - "8.2"
+ - "8.3"
+ - "8.4"
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+
+ - name: Setup cache environment
+ id: cache-env
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: ${{ env.extensions }}
+ key: ${{ env.key }}
+
+ - name: Cache extensions
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.cache-env.outputs.dir }}
+ key: ${{ steps.cache-env.outputs.key }}
+ restore-keys: ${{ steps.cache-env.outputs.key }}
+
+ - name: Install PHP with extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: ${{ env.extensions }}
+ ini-values: date.timezone='UTC'
+
+ - name: Determine composer cache directory on Linux
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV
+
+ - name: Determine composer cache directory on Windows
+ if: matrix.os == 'windows-latest'
+ run: |
+ echo "COMPOSER_CACHE_DIR=~\AppData\Local\Composer" >> $GITHUB_ENV
+
+ - name: Cache dependencies installed with composer
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.cache-env.outputs.dir }}
+ key: php${{ matrix.php }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}
+ restore-keys: |
+ php${{ matrix.php }}-composer-${{ matrix.dependencies }}-
+
+ - name: Install dependencies with composer php PHP [5.6 - 8.0]
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+
+ - name: Run tests with codeception
+ run: |
+ sed -i "s/'cookieValidationKey' => ''/'cookieValidationKey' => 'testkey'/" config/web.php
+ php -S 127.0.0.1:8080 -t public > ./runtime/yii.log 2>&1 &
+ vendor/bin/codecept run
+ shell: bash
diff --git a/.gitignore b/.gitignore
index 6084c1da5..05fb29d1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
# phpstorm project files
.idea
+# visual studio code project files
+.vscode
+
# netbeans project files
nbproject
@@ -27,4 +30,7 @@ phpunit.phar
/phpunit.xml
tests/_output/*
-tests/_support/_generated
\ No newline at end of file
+tests/_support/_generated
+
+#vagrant folder
+/.vagrant
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e51dde1fb..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-language: php
-
-php:
- - 5.4
- - 5.5
- - 5.6
- - 7.0
- - 7.1
-# - hhvm
-
-# faster builds on new travis setup not using sudo
-sudo: false
-
-# cache vendor dirs
-cache:
- directories:
- - $HOME/.composer/cache
-
-install:
- - travis_retry composer self-update && composer --version
- - travis_retry composer update --dev --prefer-dist --no-interaction
-# setup application:
- - |
- sed -i "s/'cookieValidationKey' => ''/'cookieValidationKey' => 'testkey'/" config/web.php
-
-script:
- - |
- php -S localhost:8080 -t web > /dev/null 2>&1 &
- vendor/bin/codecept run
diff --git a/LICENSE.md b/LICENSE.md
index e98f03df8..ee872b9ab 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,3 @@
-The Yii framework is free software. It is released under the terms of
-the following BSD License.
-
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
diff --git a/README.md b/README.md
index 4e852c8b5..981a7f9d9 100644
--- a/README.md
+++ b/README.md
@@ -6,16 +6,16 @@
The path to yii framework seems to be incorrect.
'; - echo 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.
'; - echo 'Please refer to the README on how to install Yii.
'; +if (!isset($frameworkPath)) { + $searchPaths = array( + dirname(__FILE__) . '/vendor/yiisoft/yii2', + dirname(__FILE__) . '/../vendor/yiisoft/yii2', + ); + foreach ($searchPaths as $path) { + if (is_dir($path)) { + $frameworkPath = $path; + break; + } + } +} + +if (!isset($frameworkPath) || !is_dir($frameworkPath)) { + $message = "The path to yii framework seems to be incorrect.
\n" + . 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . ".
\n" + . 'Please refer to the README on how to install Yii.
\n"; + + if (!empty($_SERVER['argv'])) { + // do not print HTML when used in console mode + echo strip_tags($message); + } else { + echo $message; + } + exit(1); } require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); @@ -82,22 +106,22 @@ 'name' => 'Memcache extension', 'mandatory' => false, 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), - 'by' => 'MemCache', - 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached totrue.' : ''
+ 'by' => 'MemCache',
+ 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : ''
),
// CAPTCHA:
array(
'name' => 'GD PHP extension with FreeType support',
'mandatory' => false,
'condition' => $gdOK,
- 'by' => 'Captcha',
+ 'by' => 'Captcha',
'memo' => $gdMemo,
),
array(
'name' => 'ImageMagick PHP extension with PNG support',
'mandatory' => false,
'condition' => $imagickOK,
- 'by' => 'Captcha',
+ 'by' => 'Captcha',
'memo' => $imagickMemo,
),
// PHP ini :
@@ -130,8 +154,10 @@
'name' => 'APC extension',
'mandatory' => false,
'condition' => extension_loaded('apc'),
- 'by' => 'ApcCache',
+ 'by' => 'ApcCache',
);
}
-$requirementsChecker->checkYii()->check($requirements)->render();
+$result = $requirementsChecker->checkYii()->check($requirements)->getResult();
+$requirementsChecker->render();
+exit($result['summary']['errors'] === 0 ? 0 : 1);
diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php
index 969c089b9..4b8424f73 100644
--- a/tests/_bootstrap.php
+++ b/tests/_bootstrap.php
@@ -1,6 +1,7 @@
amOnPage(Url::toRoute('/site/contact'));
}
-
+
public function contactPageWorks(AcceptanceTester $I)
{
$I->wantTo('ensure that contact page works');
@@ -25,7 +25,7 @@ public function contactFormCanBeSubmitted(AcceptanceTester $I)
$I->fillField('#contactform-verifycode', 'testme');
$I->click('contact-button');
-
+
$I->wait(2); // wait for button to be clicked
$I->dontSeeElement('#contact-form');
diff --git a/tests/acceptance/HomeCest.php b/tests/acceptance/HomeCest.php
index cb8faea71..98fb6b9f7 100644
--- a/tests/acceptance/HomeCest.php
+++ b/tests/acceptance/HomeCest.php
@@ -1,17 +1,18 @@
amOnPage(Url::toRoute('/site/index'));
+ $I->amOnPage(Url::toRoute('/site/index'));
$I->see('My Company');
-
+
$I->seeLink('About');
$I->click('About');
$I->wait(2); // wait for page to be opened
-
+
$I->see('This is the About page.');
}
}
diff --git a/tests/acceptance/LoginCest.php b/tests/acceptance/LoginCest.php
index 9ffc395ba..6f5cb2f38 100644
--- a/tests/acceptance/LoginCest.php
+++ b/tests/acceptance/LoginCest.php
@@ -1,5 +1,6 @@
-rem @link http://www.yiiframework.com/
+rem @link https://www.yiiframework.com/
rem @copyright Copyright (c) 2008 Yii Software LLC
-rem @license http://www.yiiframework.com/license/
+rem @license https://www.yiiframework.com/license/
rem -------------------------------------------------------------
@setlocal
diff --git a/tests/functional.suite.yml b/tests/functional.suite.yml
index 374c6df45..9d8cf149c 100644
--- a/tests/functional.suite.yml
+++ b/tests/functional.suite.yml
@@ -6,8 +6,9 @@
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
#basic/web/index.php
-class_name: FunctionalTester
+actor: FunctionalTester
modules:
enabled:
- Filesystem
- Yii2
+ - Asserts
diff --git a/tests/functional/ContactFormCest.php b/tests/functional/ContactFormCest.php
index 7f198bf6d..f1ccaf18f 100644
--- a/tests/functional/ContactFormCest.php
+++ b/tests/functional/ContactFormCest.php
@@ -1,14 +1,15 @@
amOnPage(['site/contact']);
+ $I->amOnRoute('site/contact');
}
public function openContactPage(\FunctionalTester $I)
{
- $I->see('Contact', 'h1');
+ $I->see('Contact', 'h1');
}
public function submitEmptyForm(\FunctionalTester $I)
@@ -37,7 +38,7 @@ public function submitFormWithIncorrectEmail(\FunctionalTester $I)
$I->see('Email is not a valid email address.');
$I->dontSee('Subject cannot be blank', '.help-inline');
$I->dontSee('Body cannot be blank', '.help-inline');
- $I->dontSee('The verification code is incorrect', '.help-inline');
+ $I->dontSee('The verification code is incorrect', '.help-inline');
}
public function submitFormSuccessfully(\FunctionalTester $I)
@@ -51,6 +52,6 @@ public function submitFormSuccessfully(\FunctionalTester $I)
]);
$I->seeEmailIsSent();
$I->dontSeeElement('#contact-form');
- $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
+ $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
}
}
diff --git a/tests/functional/LoginFormCest.php b/tests/functional/LoginFormCest.php
index 9ed74b113..a1545a706 100644
--- a/tests/functional/LoginFormCest.php
+++ b/tests/functional/LoginFormCest.php
@@ -1,4 +1,5 @@
see('Login', 'h1');
-
}
// demonstrates `amLoggedInAs` method
@@ -53,6 +53,6 @@ public function loginSuccessfully(\FunctionalTester $I)
'LoginForm[password]' => 'admin',
]);
$I->see('Logout (admin)');
- $I->dontSeeElement('form#login-form');
+ $I->dontSeeElement('form#login-form');
}
-}
\ No newline at end of file
+}
diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml
index b1efaba99..c14e49c37 100644
--- a/tests/unit.suite.yml
+++ b/tests/unit.suite.yml
@@ -3,9 +3,9 @@
# suite for unit (internal) tests.
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
-class_name: UnitTester
+actor: UnitTester
modules:
enabled:
- Asserts
- Yii2:
- part: [orm, email]
\ No newline at end of file
+ part: [orm, email, fixtures]
diff --git a/tests/unit/models/ContactFormTest.php b/tests/unit/models/ContactFormTest.php
index 81a67e9cc..1bb1bb2d0 100644
--- a/tests/unit/models/ContactFormTest.php
+++ b/tests/unit/models/ContactFormTest.php
@@ -1,12 +1,12 @@
model = $this->getMockBuilder('app\models\ContactForm')
- ->setMethods(['validate'])
- ->getMock();
+ $model = new ContactForm();
- $this->model->expects($this->once())
- ->method('validate')
- ->will($this->returnValue(true));
-
- $this->model->attributes = [
+ $model->attributes = [
'name' => 'Tester',
'email' => 'tester@example.com',
'subject' => 'very important letter subject',
'body' => 'body of current message',
+ 'verifyCode' => 'testme',
];
- expect_that($this->model->contact('admin@example.com'));
+ verify($model->contact('admin@example.com'))->notEmpty();
// using Yii2 module actions to check email was sent
$this->tester->seeEmailIsSent();
+ /** @var MessageInterface $emailMessage */
$emailMessage = $this->tester->grabLastSentEmail();
- expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface');
- expect($emailMessage->getTo())->hasKey('admin@example.com');
- expect($emailMessage->getFrom())->hasKey('tester@example.com');
- expect($emailMessage->getSubject())->equals('very important letter subject');
- expect($emailMessage->toString())->contains('body of current message');
+ verify($emailMessage)->instanceOf('yii\mail\MessageInterface');
+ verify($emailMessage->getTo())->arrayHasKey('admin@example.com');
+ verify($emailMessage->getFrom())->arrayHasKey('noreply@example.com');
+ verify($emailMessage->getReplyTo())->arrayHasKey('tester@example.com');
+ verify($emailMessage->getSubject())->equals('very important letter subject');
+ verify($emailMessage->toString())->stringContainsString('body of current message');
}
}
diff --git a/tests/unit/models/LoginFormTest.php b/tests/unit/models/LoginFormTest.php
index 46190cd05..6f96c2377 100644
--- a/tests/unit/models/LoginFormTest.php
+++ b/tests/unit/models/LoginFormTest.php
@@ -1,9 +1,8 @@
'not_existing_password',
]);
- expect_not($this->model->login());
- expect_that(\Yii::$app->user->isGuest);
+ verify($this->model->login())->false();
+ verify(\Yii::$app->user->isGuest)->true();
}
public function testLoginWrongPassword()
@@ -32,9 +31,9 @@ public function testLoginWrongPassword()
'password' => 'wrong_password',
]);
- expect_not($this->model->login());
- expect_that(\Yii::$app->user->isGuest);
- expect($this->model->errors)->hasKey('password');
+ verify($this->model->login())->false();
+ verify(\Yii::$app->user->isGuest)->true();
+ verify($this->model->errors)->arrayHasKey('password');
}
public function testLoginCorrect()
@@ -44,9 +43,8 @@ public function testLoginCorrect()
'password' => 'demo',
]);
- expect_that($this->model->login());
- expect_not(\Yii::$app->user->isGuest);
- expect($this->model->errors)->hasntKey('password');
+ verify($this->model->login())->true();
+ verify(\Yii::$app->user->isGuest)->false();
+ verify($this->model->errors)->arrayHasNotKey('password');
}
-
}
diff --git a/tests/unit/models/UserTest.php b/tests/unit/models/UserTest.php
index 2187ab8ca..3db9772a3 100644
--- a/tests/unit/models/UserTest.php
+++ b/tests/unit/models/UserTest.php
@@ -1,42 +1,43 @@
username)->equals('admin');
+ verify($user = User::findIdentity(100))->notEmpty();
+ verify($user->username)->equals('admin');
- expect_not(User::findIdentity(999));
+ verify(User::findIdentity(999))->empty();
}
public function testFindUserByAccessToken()
{
- expect_that($user = User::findIdentityByAccessToken('100-token'));
- expect($user->username)->equals('admin');
+ verify($user = User::findIdentityByAccessToken('100-token'))->notEmpty();
+ verify($user->username)->equals('admin');
- expect_not(User::findIdentityByAccessToken('non-existing'));
+ verify(User::findIdentityByAccessToken('non-existing'))->empty();
}
public function testFindUserByUsername()
{
- expect_that($user = User::findByUsername('admin'));
- expect_not(User::findByUsername('not-admin'));
+ verify($user = User::findByUsername('admin'))->notEmpty();
+ verify(User::findByUsername('not-admin'))->empty();
}
/**
* @depends testFindUserByUsername
*/
- public function testValidateUser($user)
+ public function testValidateUser()
{
$user = User::findByUsername('admin');
- expect_that($user->validateAuthKey('test100key'));
- expect_not($user->validateAuthKey('test102key'));
+ verify($user->validateAuthKey('test100key'))->notEmpty();
+ verify($user->validateAuthKey('test102key'))->empty();
- expect_that($user->validatePassword('admin'));
- expect_not($user->validatePassword('123456'));
+ verify($user->validatePassword('admin'))->notEmpty();
+ verify($user->validatePassword('123456'))->empty();
}
-
}
diff --git a/tests/unit/widgets/AlertTest.php b/tests/unit/widgets/AlertTest.php
new file mode 100644
index 000000000..af2db5d21
--- /dev/null
+++ b/tests/unit/widgets/AlertTest.php
@@ -0,0 +1,263 @@
+session->setFlash('error', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleErrorMessages()
+ {
+ $firstMessage = 'This is the first error message';
+ $secondMessage = 'This is the second error message';
+
+ Yii::$app->session->setFlash('error', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleDangerMessage()
+ {
+ $message = 'This is a danger message';
+
+ Yii::$app->session->setFlash('danger', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleDangerMessages()
+ {
+ $firstMessage = 'This is the first danger message';
+ $secondMessage = 'This is the second danger message';
+
+ Yii::$app->session->setFlash('danger', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleSuccessMessage()
+ {
+ $message = 'This is a success message';
+
+ Yii::$app->session->setFlash('success', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-success');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleSuccessMessages()
+ {
+ $firstMessage = 'This is the first danger message';
+ $secondMessage = 'This is the second danger message';
+
+ Yii::$app->session->setFlash('success', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-success');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleInfoMessage()
+ {
+ $message = 'This is an info message';
+
+ Yii::$app->session->setFlash('info', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-info');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleInfoMessages()
+ {
+ $firstMessage = 'This is the first info message';
+ $secondMessage = 'This is the second info message';
+
+ Yii::$app->session->setFlash('info', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-info');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleWarningMessage()
+ {
+ $message = 'This is a warning message';
+
+ Yii::$app->session->setFlash('warning', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-warning');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ }
+
+ public function testMultipleWarningMessages()
+ {
+ $firstMessage = 'This is the first warning message';
+ $secondMessage = 'This is the second warning message';
+
+ Yii::$app->session->setFlash('warning', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-warning');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ }
+
+ public function testSingleMixedMessages()
+ {
+ $errorMessage = 'This is an error message';
+ $dangerMessage = 'This is a danger message';
+ $successMessage = 'This is a success message';
+ $infoMessage = 'This is a info message';
+ $warningMessage = 'This is a warning message';
+
+ Yii::$app->session->setFlash('error', $errorMessage);
+ Yii::$app->session->setFlash('danger', $dangerMessage);
+ Yii::$app->session->setFlash('success', $successMessage);
+ Yii::$app->session->setFlash('info', $infoMessage);
+ Yii::$app->session->setFlash('warning', $warningMessage);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($errorMessage);
+ verify($renderingResult)->stringContainsString($dangerMessage);
+ verify($renderingResult)->stringContainsString($successMessage);
+ verify($renderingResult)->stringContainsString($infoMessage);
+ verify($renderingResult)->stringContainsString($warningMessage);
+
+ verify($renderingResult)->stringContainsString('alert-danger');
+ verify($renderingResult)->stringContainsString('alert-success');
+ verify($renderingResult)->stringContainsString('alert-info');
+ verify($renderingResult)->stringContainsString('alert-warning');
+ }
+
+ public function testMultipleMixedMessages()
+ {
+ $firstErrorMessage = 'This is the first error message';
+ $secondErrorMessage = 'This is the second error message';
+ $firstDangerMessage = 'This is the first danger message';
+ $secondDangerMessage = 'This is the second';
+ $firstSuccessMessage = 'This is the first success message';
+ $secondSuccessMessage = 'This is the second success message';
+ $firstInfoMessage = 'This is the first info message';
+ $secondInfoMessage = 'This is the second info message';
+ $firstWarningMessage = 'This is the first warning message';
+ $secondWarningMessage = 'This is the second warning message';
+
+ Yii::$app->session->setFlash('error', [$firstErrorMessage, $secondErrorMessage]);
+ Yii::$app->session->setFlash('danger', [$firstDangerMessage, $secondDangerMessage]);
+ Yii::$app->session->setFlash('success', [$firstSuccessMessage, $secondSuccessMessage]);
+ Yii::$app->session->setFlash('info', [$firstInfoMessage, $secondInfoMessage]);
+ Yii::$app->session->setFlash('warning', [$firstWarningMessage, $secondWarningMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstErrorMessage);
+ verify($renderingResult)->stringContainsString($secondErrorMessage);
+ verify($renderingResult)->stringContainsString($firstDangerMessage);
+ verify($renderingResult)->stringContainsString($secondDangerMessage);
+ verify($renderingResult)->stringContainsString($firstSuccessMessage);
+ verify($renderingResult)->stringContainsString($secondSuccessMessage);
+ verify($renderingResult)->stringContainsString($firstInfoMessage);
+ verify($renderingResult)->stringContainsString($secondInfoMessage);
+ verify($renderingResult)->stringContainsString($firstWarningMessage);
+ verify($renderingResult)->stringContainsString($secondWarningMessage);
+
+ verify($renderingResult)->stringContainsString('alert-danger');
+ verify($renderingResult)->stringContainsString('alert-success');
+ verify($renderingResult)->stringContainsString('alert-info');
+ verify($renderingResult)->stringContainsString('alert-warning');
+ }
+
+ public function testFlashIntegrity()
+ {
+ $errorMessage = 'This is an error message';
+ $unrelatedMessage = 'This is a message that is not related to the alert widget';
+
+ Yii::$app->session->setFlash('error', $errorMessage);
+ Yii::$app->session->setFlash('unrelated', $unrelatedMessage);
+
+ Alert::widget();
+
+ // Simulate redirect
+ Yii::$app->session->close();
+ Yii::$app->session->open();
+
+ verify(Yii::$app->session->getFlash('error'))->empty();
+ verify(Yii::$app->session->getFlash('unrelated'))->equals($unrelatedMessage);
+ }
+}
diff --git a/vagrant/config/.gitignore b/vagrant/config/.gitignore
new file mode 100644
index 000000000..0685a56ca
--- /dev/null
+++ b/vagrant/config/.gitignore
@@ -0,0 +1,2 @@
+# local configuration
+vagrant-local.yml
\ No newline at end of file
diff --git a/vagrant/config/vagrant-local.example.yml b/vagrant/config/vagrant-local.example.yml
new file mode 100644
index 000000000..919aec0e8
--- /dev/null
+++ b/vagrant/config/vagrant-local.example.yml
@@ -0,0 +1,22 @@
+# Your personal GitHub token
+github_token: