Browse Source

Merge branch '3.next'

Corey Taylor 6 years ago
parent
commit
adbe61881f

+ 1 - 1
Makefile

@@ -28,7 +28,7 @@ DASH_VERSION=$(shell echo $(VERSION) | sed -e s/\\./-/g)
 # correct tag in that repo.
 # For 3.1.x use 3.1.2
 # For 3.0.x use 3.0.5
-APP_VERSION:=master
+APP_VERSION:=3.x
 
 ALL: help
 .PHONY: help install test need-version bump-version tag-version

+ 2 - 2
src/Database/Expression/TupleComparison.php

@@ -119,8 +119,8 @@ class TupleComparison extends Comparison
             if ($isMultiOperation) {
                 $bound = [];
                 foreach ($value as $k => $val) {
-                    $valType = $type ? $type[$k] : $type;
-                    $bound[] = $this->_bindValue($val, $generator, $valType);
+                    $valType = $multiType && isset($type[$k]) ? $type[$k] : $type;
+                    $bound[] = $this->_bindValue($generator, $val, $valType);
                 }
 
                 $values[] = sprintf('(%s)', implode(',', $bound));

+ 1 - 1
src/I18n/Number.php

@@ -44,7 +44,7 @@ class Number
 
     /**
      * ICU Constant for accounting format; not yet widely supported by INTL library.
-     * This will be able to go away once CakePHP minimum PHP requirement is 7.5 or higher.
+     * This will be able to go away once CakePHP minimum PHP requirement is 7.4.1 or higher.
      * See UNUM_CURRENCY_ACCOUNTING in https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/unum_8h.html
      */
     public const CURRENCY_ACCOUNTING = 12;

+ 39 - 0
src/TestSuite/MockBuilder.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.8.8
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\TestSuite;
+
+use PHPUnit\Framework\MockObject\MockBuilder as BaseMockBuilder;
+
+/**
+ * PHPUnit MockBuilder with muted Reflection errors
+ *
+ * @internal
+ */
+class MockBuilder extends BaseMockBuilder
+{
+    /**
+     * @inheritDoc
+     */
+    public function getMock()
+    {
+        $errorLevel = error_reporting();
+        error_reporting(E_ALL ^ E_DEPRECATED);
+        try {
+            return parent::getMock();
+        } finally {
+            error_reporting($errorLevel);
+        }
+    }
+}

+ 70 - 0
src/TestSuite/TestCase.php

@@ -784,6 +784,76 @@ abstract class TestCase extends BaseTestCase
 // phpcs:enable
 
     /**
+     * @inheritDoc
+     */
+    public function getMockBuilder($className)
+    {
+        return new MockBuilder($this, $className);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function getMockClass(
+        $originalClassName,
+        $methods = [],
+        array $arguments = [],
+        $mockClassName = '',
+        $callOriginalConstructor = false,
+        $callOriginalClone = true,
+        $callAutoload = true,
+        $cloneArguments = false
+    ) {
+        $errorLevel = error_reporting();
+        error_reporting(E_ALL ^ E_DEPRECATED);
+        try {
+            return parent::getMockClass(
+                $originalClassName,
+                $methods,
+                $arguments,
+                $mockClassName,
+                $callOriginalConstructor,
+                $callOriginalClone,
+                $callAutoload,
+                $cloneArguments
+            );
+        } finally {
+            error_reporting($errorLevel);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function getMockForAbstractClass(
+        $originalClassName,
+        array $arguments = [],
+        $mockClassName = '',
+        $callOriginalConstructor = true,
+        $callOriginalClone = true,
+        $callAutoload = true,
+        $mockedMethods = [],
+        $cloneArguments = false
+    ) {
+        $errorLevel = error_reporting();
+        error_reporting(E_ALL ^ E_DEPRECATED);
+        try {
+            return parent::getMockForAbstractClass(
+                $originalClassName,
+                $arguments,
+                $mockClassName,
+                $callOriginalConstructor,
+                $callOriginalClone,
+                $callAutoload,
+                $mockedMethods,
+                $cloneArguments
+            );
+        } finally {
+            error_reporting($errorLevel);
+        }
+    }
+
+    /**
      * Mock a model, maintain fixtures and table association
      *
      * @param string $alias The model to get a mock for.

+ 3 - 0
tests/TestCase/Routing/Route/RouteTest.php

@@ -1011,6 +1011,9 @@ class RouteTest extends TestCase
         $result = $route->match(['plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 9]);
         $this->assertSame('/posts/view/9', $result);
 
+        $result = $route->match(['plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 9]);
+        $this->assertSame('/posts/view/9', $result);
+
         $result = $route->match(['plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => '9']);
         $this->assertSame('/posts/view/9', $result);
 

+ 3 - 0
tests/TestCase/Routing/RouterTest.php

@@ -156,6 +156,7 @@ class RouterTest extends TestCase
     }
 
     /**
+<<<<<<< HEAD
      * Test that full base URL can be generated from request context too if
      * App.fullBaseUrl is not set.
      *
@@ -174,6 +175,8 @@ class RouterTest extends TestCase
     }
 
     /**
+=======
+>>>>>>> 3.next
      * testRouteExists method
      *
      * @return void

+ 593 - 0
tests/TestCase/View/Helper/FormHelperTest.php

@@ -6105,9 +6105,602 @@ class FormHelperTest extends TestCase
     public function testDateTimeSecured()
     {
         $this->View->setRequest(
+<<<<<<< HEAD
             $this->View->getRequest()->withAttribute('formTokenData', ['unlockedFields' => []])
         );
         $this->Form->create();
+=======
+            $this->View->getRequest()->withParam('_Token', ['unlockedFields' => []])
+        );
+        $this->Form->dateTime('Contact.date');
+        $expected = [
+            'Contact.date.year',
+            'Contact.date.month',
+            'Contact.date.day',
+            'Contact.date.hour',
+            'Contact.date.minute',
+        ];
+        $this->assertEquals($expected, $this->Form->fields);
+
+        $this->Form->fields = [];
+        $this->Form->date('Contact.published');
+        $expected = [
+            'Contact.published.year',
+            'Contact.published.month',
+            'Contact.published.day',
+        ];
+        $this->assertEquals($expected, $this->Form->fields);
+    }
+
+    /**
+     * testDateTimeSecuredDisabled method
+     *
+     * Test that datetime fields are added to protected fields list.
+     *
+     * @return void
+     */
+    public function testDateTimeSecuredDisabled()
+    {
+        $this->View->setRequest(
+            $this->View->getRequest()->withParam('_Token', ['unlockedFields' => []])
+        );
+        $this->Form->dateTime('Contact.date', ['secure' => false]);
+        $expected = [];
+        $this->assertEquals($expected, $this->Form->fields);
+
+        $this->Form->fields = [];
+        $this->Form->date('Contact.published', ['secure' => false]);
+        $expected = [];
+        $this->assertEquals($expected, $this->Form->fields);
+    }
+
+    /**
+     * Test empty defaulting to true for datetime.
+     *
+     * @return void
+     */
+    public function testDatetimeEmpty()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->dateTime('Contact.date', [
+            'timeFormat' => 12,
+            'empty' => true,
+            'default' => true,
+        ]);
+        $expected = [
+            ['select' => ['name' => 'Contact[date][year]']],
+            $yearsRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][month]']],
+            $monthsRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][day]']],
+            $daysRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][hour]']],
+            $hoursRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][minute]']],
+            $minutesRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][meridian]']],
+            $meridianRegex,
+            ['option' => ['value' => '']],
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+        $this->assertNotRegExp('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+    }
+
+    /**
+     * Presence check for array form empty options
+     *
+     * @return void
+     */
+    public function testDateTimeEmptyAsArrayPresence()
+    {
+        $result = $this->Form->dateTime('Contact.date', [
+            'empty' => [
+                'day' => 'DAY',
+                'month' => 'MONTH',
+                'year' => 'YEAR',
+                'hour' => 'HOUR',
+                'minute' => 'MINUTE',
+                'meridian' => false,
+            ],
+            'default' => true,
+        ]);
+
+        $this->assertRegExp('/<option value="">DAY<\/option>/', $result);
+        $this->assertRegExp('/<option value="">MONTH<\/option>/', $result);
+        $this->assertRegExp('/<option value="">YEAR<\/option>/', $result);
+        $this->assertRegExp('/<option value="">HOUR<\/option>/', $result);
+        $this->assertRegExp('/<option value="">MINUTE<\/option>/', $result);
+        $this->assertNotRegExp('/<option value=""><\/option>/', $result);
+    }
+
+    /**
+     * Test datetime with array empty value, ensuring
+     * empty options aren't duplicated.
+     *
+     * @return void
+     */
+    public function testDatetimeEmptyArrayForm()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->dateTime('Contact.date', [
+            'minYear' => '2017',
+            'maxYear' => '2019',
+            'empty' => [
+                'year' => 'pick year',
+                'month' => 'pick month',
+            ],
+            'hour' => false,
+            'minute' => false,
+            'second' => false,
+            'meridian' => false,
+        ]);
+        $expected = [
+            ['select' => ['name' => 'Contact[date][year]']],
+            ['option' => ['value' => '', 'selected' => 'selected']], 'pick year', '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][month]']],
+            ['option' => ['value' => '', 'selected' => 'selected']], 'pick month', '/option',
+            $monthsRegex,
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][day]']],
+            $daysRegex,
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * Test datetime with array empty value, ensuring
+     * empty options aren't duplicated.
+     *
+     * @return void
+     */
+    public function testDatetimeEmptyArrayFormKeyValue()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->dateTime('Contact.date', [
+            'minYear' => '2017',
+            'maxYear' => '2019',
+            'empty' => [
+                '' => '-',
+            ],
+            'hour' => false,
+            'minute' => false,
+            'second' => false,
+            'meridian' => false,
+        ]);
+        $expected = [
+            ['select' => ['name' => 'Contact[date][year]']],
+            ['option' => ['value' => '', 'selected' => 'selected']], '-', '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][month]']],
+            ['option' => ['value' => '', 'selected' => 'selected']], '-', '/option',
+            $monthsRegex,
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][day]']],
+            ['option' => ['value' => '', 'selected' => 'selected']], '-', '/option',
+            $daysRegex,
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * testDatetimeMinuteInterval method
+     *
+     * Test datetime with interval option.
+     *
+     * @return void
+     */
+    public function testDatetimeMinuteInterval()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->dateTime('Contact.date', [
+            'interval' => 5,
+            'value' => '',
+        ]);
+        $expected = [
+            ['select' => ['name' => 'Contact[date][year]']],
+            $yearsRegex,
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][month]']],
+            $monthsRegex,
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][day]']],
+            $daysRegex,
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][hour]']],
+            $hoursRegex,
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            '*/select',
+
+            ['select' => ['name' => 'Contact[date][minute]']],
+            $minutesRegex,
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            ['option' => ['value' => '00']],
+            '00',
+            '/option',
+            ['option' => ['value' => '05']],
+            '05',
+            '/option',
+            ['option' => ['value' => '10']],
+            '10',
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * testDateTimeRounding method
+     *
+     * Test dateTime with rounding.
+     *
+     * @return void
+     */
+    public function testDateTimeRounding()
+    {
+        $this->View->setRequest($this->View->getRequest()->withData('Contact', [
+            'date' => [
+                'day' => '13',
+                'month' => '12',
+                'year' => '2010',
+                'hour' => '04',
+                'minute' => '19',
+                'meridian' => 'AM',
+            ],
+        ]));
+
+        $result = $this->Form->dateTime('Contact.date', ['interval' => 15]);
+        $this->assertTextContains('<option value="15" selected="selected">15</option>', $result);
+
+        $result = $this->Form->dateTime('Contact.date', ['interval' => 15, 'round' => 'up']);
+        $this->assertTextContains('<option value="30" selected="selected">30</option>', $result);
+
+        $result = $this->Form->dateTime('Contact.date', ['interval' => 5, 'round' => 'down']);
+        $this->assertTextContains('<option value="15" selected="selected">15</option>', $result);
+    }
+
+    /**
+     * testDatetimeWithDefault method
+     *
+     * Test that datetime() and default values work.
+     *
+     * @return void
+     */
+    public function testDatetimeWithDefault()
+    {
+        $result = $this->Form->dateTime('Contact.updated', ['value' => '2009-06-01 11:15:30']);
+        $this->assertRegExp('/<option[^<>]+value="2009"[^<>]+selected="selected"[^>]*>2009<\/option>/', $result);
+        $this->assertRegExp('/<option[^<>]+value="01"[^<>]+selected="selected"[^>]*>1<\/option>/', $result);
+        $this->assertRegExp('/<option[^<>]+value="06"[^<>]+selected="selected"[^>]*>June<\/option>/', $result);
+
+        $result = $this->Form->dateTime('Contact.updated', [
+            'default' => '2009-06-01 11:15:30',
+        ]);
+        $this->assertRegExp('/<option[^<>]+value="2009"[^<>]+selected="selected"[^>]*>2009<\/option>/', $result);
+        $this->assertRegExp('/<option[^<>]+value="01"[^<>]+selected="selected"[^>]*>1<\/option>/', $result);
+        $this->assertRegExp('/<option[^<>]+value="06"[^<>]+selected="selected"[^>]*>June<\/option>/', $result);
+    }
+
+    /**
+     * testDateTimeAllZeros method
+     *
+     * Test datetime() with all zeros.
+     *
+     * @return void
+     */
+    public function testDateTimeAllZeros()
+    {
+        $result = $this->Form->dateTime('Contact.date', [
+            'timeFormat' => false,
+            'empty' => ['day' => '-', 'month' => '-', 'year' => '-'],
+            'value' => '0000-00-00',
+        ]);
+
+        $this->assertRegExp('/<option value="">-<\/option>/', $result);
+        $this->assertNotRegExp('/<option value="0" selected="selected">0<\/option>/', $result);
+    }
+
+    /**
+     * testFormDateTimeMulti method
+     *
+     * Test multiple datetime element generation.
+     *
+     * @return void
+     */
+    public function testFormDateTimeMulti()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->dateTime('Contact.1.updated');
+        $this->assertContains('Contact[1][updated][month]', $result);
+        $this->assertContains('Contact[1][updated][day]', $result);
+        $this->assertContains('Contact[1][updated][year]', $result);
+        $this->assertContains('Contact[1][updated][hour]', $result);
+        $this->assertContains('Contact[1][updated][minute]', $result);
+    }
+
+    /**
+     * testDateTimeLabelIdMatchesFirstControl method
+     *
+     * When changing the date format, the label should always focus the first select box when
+     * clicked.
+     *
+     * @return void
+     */
+    public function testDateTimeLabelIdMatchesFirstControl()
+    {
+        $result = $this->Form->control('Model.date', ['type' => 'date']);
+        $this->assertContains('<label>Date</label>', $result);
+
+        $result = $this->Form->control('Model.date', ['type' => 'date', 'dateFormat' => 'DMY']);
+        $this->assertContains('<label>Date</label>', $result);
+
+        $result = $this->Form->control('Model.date', ['type' => 'date', 'dateFormat' => 'YMD']);
+        $this->assertContains('<label>Date</label>', $result);
+    }
+
+    /**
+     * testDateTimeSecondOptions method
+     *
+     * Test datetime second=true.
+     *
+     * @return void
+     */
+    public function testDateTimeSecondOptions()
+    {
+        $result = $this->Form->dateTime('updated', ['second' => true]);
+        $this->assertContains('updated[second]', $result, 'Should have seconds');
+
+        $result = $this->Form->dateTime('updated', ['second' => []]);
+        $this->assertContains('updated[second]', $result, 'Should have seconds');
+
+        $result = $this->Form->dateTime('updated', ['second' => null]);
+        $this->assertNotContains('updated[second]', $result, 'Should not have seconds');
+
+        $result = $this->Form->dateTime('updated', ['second' => false]);
+        $this->assertNotContains('updated[second]', $result, 'Should not have seconds');
+    }
+
+    /**
+     * testMonth method
+     *
+     * Test generation of a month input.
+     *
+     * @return void
+     */
+    public function testMonth()
+    {
+        $result = $this->Form->month('Model.field', ['value' => '']);
+        $expected = [
+            ['select' => ['name' => 'Model[field][month]']],
+            ['option' => ['value' => '', 'selected' => 'selected']],
+            '/option',
+            ['option' => ['value' => '01']],
+            date('F', strtotime('2008-01-01 00:00:00')),
+            '/option',
+            ['option' => ['value' => '02']],
+            date('F', strtotime('2008-02-01 00:00:00')),
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->month('Model.field', ['empty' => true, 'value' => '']);
+        $expected = [
+            ['select' => ['name' => 'Model[field][month]']],
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            date('F', strtotime('2008-01-01 00:00:00')),
+            '/option',
+            ['option' => ['value' => '02']],
+            date('F', strtotime('2008-02-01 00:00:00')),
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->month('Model.field', ['value' => '', 'monthNames' => false]);
+        $expected = [
+            ['select' => ['name' => 'Model[field][month]']],
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            '1',
+            '/option',
+            ['option' => ['value' => '02']],
+            '2',
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $monthNames = [
+            '01' => 'Jan', '02' => 'Feb', '03' => 'Mar', '04' => 'Apr', '05' => 'May', '06' => 'Jun',
+            '07' => 'Jul', '08' => 'Aug', '09' => 'Sep', '10' => 'Oct', '11' => 'Nov', '12' => 'Dec',
+        ];
+        $result = $this->Form->month('Model.field', ['value' => '1', 'monthNames' => $monthNames]);
+        $expected = [
+            ['select' => ['name' => 'Model[field][month]']],
+            ['option' => ['value' => '']],
+            '/option',
+            ['option' => ['value' => '01', 'selected' => 'selected']],
+            'Jan',
+            '/option',
+            ['option' => ['value' => '02']],
+            'Feb',
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->View->setRequest(
+            $this->View->getRequest()->withData('Project.release', '2050-02-10')
+        );
+        $this->Form->create();
+        $result = $this->Form->month('Project.release');
+
+        $expected = [
+            ['select' => ['name' => 'Project[release][month]']],
+            ['option' => ['value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            'January',
+            '/option',
+            ['option' => ['value' => '02', 'selected' => 'selected']],
+            'February',
+            '/option',
+            '*/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->month('Contact.published', [
+            'empty' => 'Published on',
+        ]);
+        $this->assertContains('Published on', $result);
+    }
+
+    /**
+     * testDay method
+     *
+     * Test generation of a day input.
+     *
+     * @return void
+     */
+    public function testDay()
+    {
+        extract($this->dateRegex);
+
+        $result = $this->Form->day('Model.field', ['value' => '', 'class' => 'form-control']);
+        $expected = [
+            ['select' => ['name' => 'Model[field][day]', 'class' => 'form-control']],
+            ['option' => ['selected' => 'selected', 'value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            '1',
+            '/option',
+            ['option' => ['value' => '02']],
+            '2',
+            '/option',
+            $daysRegex,
+            '/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->View->setRequest(
+            $this->View->getRequest()->withData('Model.field', '2006-10-10 23:12:32')
+        );
+        $this->Form->create();
+        $result = $this->Form->day('Model.field');
+        $expected = [
+            ['select' => ['name' => 'Model[field][day]']],
+            ['option' => ['value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            '1',
+            '/option',
+            ['option' => ['value' => '02']],
+            '2',
+            '/option',
+            $daysRegex,
+            ['option' => ['value' => '10', 'selected' => 'selected']],
+            '10',
+            '/option',
+            $daysRegex,
+            '/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->View->setRequest($this->View->getRequest()->withData('Model.field', ''));
+        $this->Form->create();
+        $result = $this->Form->day('Model.field', ['value' => '10']);
+        $expected = [
+            ['select' => ['name' => 'Model[field][day]']],
+            ['option' => ['value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            '1',
+            '/option',
+            ['option' => ['value' => '02']],
+            '2',
+            '/option',
+            $daysRegex,
+            ['option' => ['value' => '10', 'selected' => 'selected']],
+            '10',
+            '/option',
+            $daysRegex,
+            '/select',
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->View->setRequest(
+            $this->View->getRequest()->withData('Project.release', '2050-10-10')
+        );
+        $this->Form->create();
+        $result = $this->Form->day('Project.release');
+
+        $expected = [
+            ['select' => ['name' => 'Project[release][day]']],
+            ['option' => ['value' => '']],
+            '/option',
+            ['option' => ['value' => '01']],
+            '1',
+            '/option',
+            ['option' => ['value' => '02']],
+            '2',
+            '/option',
+            $daysRegex,
+            ['option' => ['value' => '10', 'selected' => 'selected']],
+            '10',
+            '/option',
+            $daysRegex,
+            '/select',
+        ];
+        $this->assertHtml($expected, $result);
+>>>>>>> 3.next
 
         $this->Form->dateTime('date');
         $expected = ['date'];