Commit 0367cebc by Alexander Makarov

Merge branch 'master'

Conflicts: framework/CHANGELOG.md
parents 1444e148 f51d0180
...@@ -284,4 +284,101 @@ Formatters ...@@ -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 In order to use formatters you need to install and enable [intl](http://www.php.net/manual/en/intro.intl.php) PHP
extension. 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. TBD: provided classes overview.
...@@ -41,6 +41,11 @@ and application destroy after each test. You can configure a mock application us ...@@ -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. `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: 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
<?php <?php
#in your unit-test #in your unit-test
...@@ -53,8 +58,10 @@ You also can use all guy methods by accessing guy instance like: ...@@ -53,8 +58,10 @@ You also can use all guy methods by accessing guy instance like:
<?php <?php
$this->codeGuy->someMethodFromModule(); $this->codeGuy->someMethodFromModule();
``` ```
Codeception events
------------------
to fire event do this: To fire event do this:
```php ```php
<?php <?php
...@@ -68,6 +75,10 @@ public function testSomething() ...@@ -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, 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```. for example ```myevent.somegroup```.
Special test method chaining
----------------------------
Execution of special tests methods is (for example on ```UserTest``` class): Execution of special tests methods is (for example on ```UserTest``` class):
``` ```
...@@ -88,49 +99,121 @@ tests\unit\models\UserTest::tearDownAfterClass(); ...@@ -88,49 +99,121 @@ tests\unit\models\UserTest::tearDownAfterClass();
If you use special methods dont forget to call its parent. 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
<?php <?php
SomeConsoleTest extends \yii\codeception\TestCase SomeConsoleTest extends \yii\codeception\TestCase
{ {
// this is the config file to load as application config // this is the config file to load as application config
public static $applicationConfig = '@app/config/web.php'; public $appConfig = '@app/path/to/my/custom/config/for/test.php';
// this defines the application class to use for mock applications
protected $applicationClass = 'yii\web\Application';
} }
``` ```
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
<?php return yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../config/console.php'),
yii\codeception\TestCase::$applicationConfig = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../_config.php'),
require(__DIR__ . '/../../config/web.php'), [
require(__DIR__ . '/../../config/codeception/unit.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:
```php
You also can reconfigure some components for tests, for this purpose there is a `$config` property in the `TestCase` class. 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
<?php <?php
SomeOtherTest extends \yii\codeception\TestCase use \yii\codeception\TestCase;
use Yii;
class MailTest extends TestCase
{ {
public $config = [
'components' => [ protected function setUp()
'mail' => [ {
'useFileTransport' => true, // 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' => [
'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 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: `Codeception\Util\Debug::debug()` function and then run test with `--debug` key, for example:
......
...@@ -87,6 +87,8 @@ Yii Framework 2 Change Log ...@@ -87,6 +87,8 @@ Yii Framework 2 Change Log
- View now falls back to `en` from `en-US` if file not found (samdark) - View now falls back to `en` from `en-US` if file not found (samdark)
- Default `sourceLanguage` and `language` are now `en` (samdark) - Default `sourceLanguage` and `language` are now `en` (samdark)
- Enh #2101: Gii is now using model labels when generating search (thiagotalma) - 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 #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 #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) - 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 ...@@ -118,6 +120,7 @@ Yii Framework 2 Change Log
- Chg #2025: Removed ability to declare scopes in ActiveRecord (samdark) - 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 #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 #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 #2161: Added ability to use `return` in `Widget::run` (samdark)
- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue) - Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue) - Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
......
...@@ -28,12 +28,11 @@ use yii\helpers\Html; ...@@ -28,12 +28,11 @@ use yii\helpers\Html;
class Formatter extends Component 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) * 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`. * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
* Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones. * 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 this property is not set, [[\yii\base\Application::timezone]] will be used.
* If not set, [[\yii\base\Application::timezone]] will be used.
*/ */
public $timeZone; public $timeZone;
/** /**
...@@ -344,7 +343,7 @@ class Formatter extends Component ...@@ -344,7 +343,7 @@ class Formatter extends Component
*/ */
protected function formatTimestamp($value, $format) 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); $date->setTimestamp($value);
return $date->format($format); return $date->format($format);
} }
......
...@@ -29,9 +29,13 @@ class I18N extends Component ...@@ -29,9 +29,13 @@ class I18N extends Component
{ {
/** /**
* @var array list of [[MessageSource]] configurations or objects. The array keys are message * @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 * category patterns, 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 * for creating the [[MessageSource]] objects.
* to match multiple categories with the same prefix. For example, 'app\*' matches both 'app\cat1' and 'app\cat2'. *
* 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 * This property may be modified on the fly by extensions who want to have their own message sources
* registered under their own namespaces. * registered under their own namespaces.
...@@ -169,15 +173,24 @@ class I18N extends Component ...@@ -169,15 +173,24 @@ class I18N extends Component
} }
} else { } else {
// try wildcard matching // try wildcard matching
foreach ($this->translations as $pattern => $config) { foreach ($this->translations as $pattern => $source) {
if ($pattern === '*' || substr($pattern, -1) === '*' && strpos($category, rtrim($pattern, '*')) === 0) { if (strpos($pattern, '*') > 0 && strpos($category, rtrim($pattern, '*')) === 0) {
if ($config instanceof MessageSource) { if ($source instanceof MessageSource) {
return $config; return $source;
} else { } 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);
}
}
} }
throw new InvalidConfigException("Unable to locate message source for category '$category'."); throw new InvalidConfigException("Unable to locate message source for category '$category'.");
......
...@@ -57,9 +57,11 @@ class DetailView extends Widget ...@@ -57,9 +57,11 @@ class DetailView extends Widget
* @var array a list of attributes to be displayed in the detail view. Each array element * @var array a list of attributes to be displayed in the detail view. Each array element
* represents the specification for displaying one particular attribute. * 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 * An attribute can be specified as a string in the format of "name", "name:format" or "name:format:label",
* the attribute name, and "Format" represents the format of the attribute. The "Format" is passed to the [[Formatter::format()]] * where "name" refers to the attribute name, and "format" represents the format of the attribute. The "format"
* method to format an attribute value into a displayable text. Please refer to [[Formatter]] for the supported types. * 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: * An attribute can also be specified in terms of an array with the following elements:
* *
...@@ -173,12 +175,13 @@ class DetailView extends Widget ...@@ -173,12 +175,13 @@ class DetailView extends Widget
foreach ($this->attributes as $i => $attribute) { foreach ($this->attributes as $i => $attribute) {
if (is_string($attribute)) { if (is_string($attribute)) {
if (!preg_match('/^(\w+)(\s*:\s*(\w+))?$/', $attribute, $matches)) { if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $attribute, $matches)) {
throw new InvalidConfigException('The attribute must be specified in the format of "Name" or "Name:Format"'); throw new InvalidConfigException('The attribute must be specified in the format of "name", "name:format" or "name:format:label"');
} }
$attribute = [ $attribute = [
'name' => $matches[1], 'name' => $matches[1],
'format' => isset($matches[3]) ? $matches[3] : 'text', '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