Commit 0367cebc by Alexander Makarov

Merge branch 'master'

Conflicts: framework/CHANGELOG.md
parents 1444e148 f51d0180
......@@ -284,4 +284,101 @@ Formatters
In order to use formatters you need to install and enable [intl](http://www.php.net/manual/en/intro.intl.php) PHP
extension.
Examples
--------
###Translating module messages
If you want to translate messages for a module and avoid using a single translation file for all messages, you can make it like the following:
```php
<?php
namespace app\modules\users;
use Yii;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'app\modules\users\controllers';
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en',
'basePath' => '@app/modules/users/messages',
'fileMap' => [
'modules/users/validation' => 'validation.php',
'modules/users/form' => 'form.php',
...
],
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
}
```
In the example above we are using wildcard for matching and then filtering each category per needed file.
###Translating widgets messages
Same rules can be applied for widgets too, for example:
```php
<?php
namespace app\widgets\menu;
use yii\base\Widget;
use Yii;
class Menu extends Widget
{
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
$i18n = Yii::$app->i18n;
$i18n->translations['widgets/menu/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en',
'basePath' => '@app/widgets/menu/messages',
'fileMap' => [
'widgets/menu/messages' => 'messages.php',
],
];
}
public function run()
{
echo $this->render('index');
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('widgets/menu/' . $category, $message, $params, $language);
}
}
```
> **Note**: For widgets you also can use i18n views, same rules as for controllers are applied to them too.
TBD: provided classes overview.
......@@ -41,6 +41,11 @@ and application destroy after each test. You can configure a mock application us
`TestCase` is extended from `Codeception\TestCase\Case` so all methods and assertions are available.
You may use codeception modules and fire events in your test, just use methods:
Getting Codeception modules
---------------------------
If you want to use codeception modules and helpers in your unit tests, you can do it like this:
```php
<?php
#in your unit-test
......@@ -53,8 +58,10 @@ You also can use all guy methods by accessing guy instance like:
<?php
$this->codeGuy->someMethodFromModule();
```
Codeception events
------------------
to fire event do this:
To fire event do this:
```php
<?php
......@@ -68,6 +75,10 @@ public function testSomething()
this event can be catched in modules and helpers. If your test is in the group, then event name will be followed by the groupname,
for example ```myevent.somegroup```.
Special test method chaining
----------------------------
Execution of special tests methods is (for example on ```UserTest``` class):
```
......@@ -88,49 +99,121 @@ tests\unit\models\UserTest::tearDownAfterClass();
If you use special methods dont forget to call its parent.
Customizing application config
------------------------------
You may need to specify different configuration files per test cases, to do this you can make it like the following:
```php
<?php
SomeConsoleTest extends \yii\codeception\TestCase
{
// this is the config file to load as application config
public static $applicationConfig = '@app/config/web.php';
// this defines the application class to use for mock applications
protected $applicationClass = 'yii\web\Application';
public $appConfig = '@app/path/to/my/custom/config/for/test.php';
}
```
The `$applicationConfig` property may be set for all tests in a `_bootstrap.php` file like this:
The `$appConfig` property could be an array or a valid alias, pointing to the file that returns a config array. You can specify
application class in the config, for example for testing console commands or features you can create `_console.php` config under
`tests/unit` directory like this:
```php
<?php
yii\codeception\TestCase::$applicationConfig = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../config/web.php'),
require(__DIR__ . '/../../config/codeception/unit.php')
return yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../config/console.php'),
require(__DIR__ . '/../_config.php'),
[
'class' => 'yii\console\Application',
'components' => [
//override console components if needed
],
]
);
```
Don't forget that you have to include autoload and Yii class in the `_bootstrap.php` file.
and then just use your `ConsoleTestCase` like the following:
You also can reconfigure some components for tests, for this purpose there is a `$config` property in the `TestCase` class.
```php
use \yii\codeception\TestCase;
class ConsoleTestCase extends TestCase
{
public $appConfig = '@tests/unit/_console.php';
}
```
You can extend other console test cases from this basic `ConsoleTestCase`.
Reconfiguring components for testing
------------------------------------
You can reconfigure a component for testing, for this purpose in your `setUp` method of your test case
you can do this for example:
```php
<?php
SomeOtherTest extends \yii\codeception\TestCase
use \yii\codeception\TestCase;
use Yii;
class MailTest extends TestCase
{
public $config = [
protected function setUp()
{
// don't forget to call parent method that will setup Yii application
parent::setUp();
Yii::$app->mail->fileTransportCallback = function ($mailer, $message) {
return 'testing_message.eml';
};
}
}
```
You don't need to worry about application instances and isolation because application will be created [each time](https://github.com/yiisoft/yii2/blob/master/extensions/codeception/TestCase.php#L31) before any of test method will be executed in test case.
You can mock application in a different way. For this purposes you have method [`mockApplication`](https://github.com/yiisoft/yii2/blob/master/extensions/codeception/TestCase.php#L55) available in your test case.
This method creates new application instance and replaces old one with it and is handy when you need to create application with a config that is different from other test methods in the current test suite. For example:
```php
use \yii\codeception\TestCase;
class SomeMyTest extends TestCase
{
public function testOne()
{
...
}
public function testTwo()
{
$this->mockApplication([
'language' => 'ru-RU',
'components' => [
'mail' => [
'useFileTransport' => true,
'db' => [
//your custom configuration here
],
]
];
],
]);
//your expectations and assertions goes here
}
public function testThree()
{
...
}
}
```
Additional debug output
-----------------------
Because of Codeception buffers all output you can't make simple `var_dump()` in the TestCase, instead you need to use
`Codeception\Util\Debug::debug()` function and then run test with `--debug` key, for example:
......
......@@ -87,6 +87,8 @@ Yii Framework 2 Change Log
- View now falls back to `en` from `en-US` if file not found (samdark)
- Default `sourceLanguage` and `language` are now `en` (samdark)
- Enh #2101: Gii is now using model labels when generating search (thiagotalma)
- Enh #2102: DetailView now allow use `category.name` as attribute name (creocoder)
- Enh #2102: DetailView now allow use custom label in string format like `name:format:label` (creocoder)
- Enh #2103: Renamed AccessDeniedHttpException to ForbiddenHttpException, added new commonly used HTTP exception classes (danschmidt5189)
- Enh #2124: Added support for UNION ALL queries (Ivan Pomortsev, iworker)
- Enh #2132: Allow url of CSS and JS files registered in yii\web\View to be url alias (cebe)
......@@ -118,6 +120,7 @@ Yii Framework 2 Change Log
- Chg #2025: Removed ability to declare scopes in ActiveRecord (samdark)
- Chg #2057: AutoTimestamp attributes defaults changed from `create_time` and `update_time` to `created_at` and `updated_at` (creocoder)
- Chg #2063: Removed `yii\web\Request::acceptTypes` and renamed `yii\web\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)
- Chg #2157: The '*' category pattern will match all categories that do not match any other patterns listed in `I18N::translations` (qiangxue, Ragazzo)
- Chg #2161: Added ability to use `return` in `Widget::run` (samdark)
- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
......
......@@ -28,12 +28,11 @@ use yii\helpers\Html;
class Formatter extends Component
{
/**
* @var string|\IntlTimeZone|\DateTimeZone the timezone to use for formatting time and date values.
* @var string the timezone to use for formatting time and date values.
* This can be any value that may be passed to [date_default_timezone_set()](http://www.php.net/manual/en/function.date-default-timezone-set.php)
* e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
* Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
* This can also be an IntlTimeZone or a DateTimeZone object.
* If not set, [[\yii\base\Application::timezone]] will be used.
* If this property is not set, [[\yii\base\Application::timezone]] will be used.
*/
public $timeZone;
/**
......@@ -344,7 +343,7 @@ class Formatter extends Component
*/
protected function formatTimestamp($value, $format)
{
$date = new DateTime(null, is_string($this->timeZone) ? new \DateTimeZone($this->timeZone) : $this->timeZone);
$date = new DateTime(null, new \DateTimeZone($this->timeZone));
$date->setTimestamp($value);
return $date->format($format);
}
......
......@@ -29,9 +29,13 @@ class I18N extends Component
{
/**
* @var array list of [[MessageSource]] configurations or objects. The array keys are message
* categories, and the array values are the corresponding [[MessageSource]] objects or the configurations
* for creating the [[MessageSource]] objects. The message categories can contain the wildcard '*' at the end
* to match multiple categories with the same prefix. For example, 'app\*' matches both 'app\cat1' and 'app\cat2'.
* category patterns, and the array values are the corresponding [[MessageSource]] objects or the configurations
* for creating the [[MessageSource]] objects.
*
* The message category patterns can contain the wildcard '*' at the end to match multiple categories with the same prefix.
* For example, 'app\*' matches both 'app\cat1' and 'app\cat2'.
*
* The '*' category pattern will match all categories that do not match any other category patterns.
*
* This property may be modified on the fly by extensions who want to have their own message sources
* registered under their own namespaces.
......@@ -169,14 +173,23 @@ class I18N extends Component
}
} else {
// try wildcard matching
foreach ($this->translations as $pattern => $config) {
if ($pattern === '*' || substr($pattern, -1) === '*' && strpos($category, rtrim($pattern, '*')) === 0) {
if ($config instanceof MessageSource) {
return $config;
foreach ($this->translations as $pattern => $source) {
if (strpos($pattern, '*') > 0 && strpos($category, rtrim($pattern, '*')) === 0) {
if ($source instanceof MessageSource) {
return $source;
} else {
return $this->translations[$category] = $this->translations[$pattern] = Yii::createObject($config);
return $this->translations[$category] = $this->translations[$pattern] = Yii::createObject($source);
}
}
}
// match '*' in the last
if (isset($this->translations['*'])) {
$source = $this->translations['*'];
if ($source instanceof MessageSource) {
return $source;
} else {
return $this->translations[$category] = $this->translations['*'] = Yii::createObject($source);
}
}
}
......
......@@ -57,9 +57,11 @@ class DetailView extends Widget
* @var array a list of attributes to be displayed in the detail view. Each array element
* represents the specification for displaying one particular attribute.
*
* An attribute can be specified as a string in the format of "Name" or "Name:Format", where "Name" refers to
* the attribute name, and "Format" represents the format of the attribute. The "Format" is passed to the [[Formatter::format()]]
* method to format an attribute value into a displayable text. Please refer to [[Formatter]] for the supported types.
* An attribute can be specified as a string in the format of "name", "name:format" or "name:format:label",
* where "name" refers to the attribute name, and "format" represents the format of the attribute. The "format"
* is passed to the [[Formatter::format()]] method to format an attribute value into a displayable text.
* Please refer to [[Formatter]] for the supported types. Both "format" and "label" are optional.
* They will take default values if absent.
*
* An attribute can also be specified in terms of an array with the following elements:
*
......@@ -173,12 +175,13 @@ class DetailView extends Widget
foreach ($this->attributes as $i => $attribute) {
if (is_string($attribute)) {
if (!preg_match('/^(\w+)(\s*:\s*(\w+))?$/', $attribute, $matches)) {
throw new InvalidConfigException('The attribute must be specified in the format of "Name" or "Name:Format"');
if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $attribute, $matches)) {
throw new InvalidConfigException('The attribute must be specified in the format of "name", "name:format" or "name:format:label"');
}
$attribute = [
'name' => $matches[1],
'format' => isset($matches[3]) ? $matches[3] : 'text',
'label' => isset($matches[5]) ? $matches[5] : null,
];
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment