Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix remaining mutants #354

Merged
merged 10 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
os: >-
['ubuntu-latest']
php: >-
['8.2']
['8.3']
bin-name: infection
secrets:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
2 changes: 1 addition & 1 deletion src/Field/Base/PartsField.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ final public function tokens(array $tokens): static
throw new InvalidArgumentException(
sprintf(
'Token should be string. %s given.',
$token,
get_debug_type($token),
)
);
}
Expand Down
9 changes: 4 additions & 5 deletions src/Field/Checkbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Yiisoft\Html\Html;

use function is_bool;
use function is_object;
use function is_string;

/**
Expand Down Expand Up @@ -211,15 +210,15 @@ public function uncheckValue(bool|float|int|string|Stringable|null $value): self
protected function generateInput(): string
{
$value = $this->getValue();

if (!is_bool($value)
if (
!is_bool($value)
&& !is_string($value)
&& !$value instanceof Stringable
&& !is_numeric($value)
&& $value !== null
&& (!is_object($value) || !method_exists($value, '__toString'))
) {
throw new InvalidArgumentException(
'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
'Checkbox widget requires a string, Stringable, numeric, bool or null value.'
);
}

Expand Down
8 changes: 4 additions & 4 deletions src/Field/RadioList.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,15 @@ protected function generateInput(): string
}

$value = $this->getValue();

if (!is_bool($value)
if (
!is_bool($value)
&& !is_string($value)
&& !$value instanceof Stringable
&& !is_numeric($value)
&& $value !== null
&& (!is_object($value) || !method_exists($value, '__toString'))
) {
throw new InvalidArgumentException(
'"RadioList" field requires a string, numeric, bool, Stringable or null value.'
'"RadioList" field requires a string, Stringable, numeric, bool or null value.'
);
}
/** @psalm-var Stringable|scalar $value */
Expand Down
7 changes: 4 additions & 3 deletions src/Field/Select.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,15 @@ protected function generateInput(): string
);
}
} else {
if (!is_bool($value)
if (
!is_bool($value)
&& !is_string($value)
&& !$value instanceof Stringable
&& !is_numeric($value)
&& $value !== null
&& (!is_object($value) || !method_exists($value, '__toString'))
) {
throw new InvalidArgumentException(
'Non-multiple Select field requires a string, numeric, bool, Stringable or null value.'
'Non-multiple Select field requires a string, Stringable, numeric, bool or null value.'
);
}
$value = $value === null ? [] : [$value];
Expand Down
31 changes: 30 additions & 1 deletion tests/Field/Base/PartsFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use stdClass;
use Yiisoft\Form\Tests\Support\StringableObject;
use Yiisoft\Form\Tests\Support\StubPartsField;
use Yiisoft\Form\Theme\ThemeContainer;

Expand Down Expand Up @@ -120,6 +121,15 @@ public function testBuiltinToken(): void
$field->token('{hint}', 'hello');
}

public function testBuiltinTokens(): void
{
$field = StubPartsField::widget();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Token name "{hint}" is built-in.');
$field->tokens(['{hint}' => 'hello']);
}

public function testEmptyToken(): void
{
$field = StubPartsField::widget();
Expand All @@ -129,12 +139,21 @@ public function testEmptyToken(): void
$field->token('', 'hello');
}

public function testEmptyTokens(): void
{
$field = StubPartsField::widget();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Token must be non-empty string.');
$field->tokens(['' => 'hello']);
}

public function testNonStringTokenName(): void
{
$field = StubPartsField::widget();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Token should be string. 0 given.');
$this->expectExceptionMessage('Token should be string. int given.');
$field->tokens(['hello']);
}

Expand All @@ -147,6 +166,16 @@ public function testNonStringTokenValue(): void
$field->tokens(['{before}' => new stdClass()]);
}

public function testStringableTokenValue(): void
{
$actualHtml = StubPartsField::widget()
->tokens(['{custom}' => new StringableObject('value')])
->template('{custom}')
->useContainer(false)
->render();
$this->assertSame('value', $actualHtml);
}

public function testHideLabel(): void
{
$result = StubPartsField::widget()
Expand Down
17 changes: 16 additions & 1 deletion tests/Field/CheckboxTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -615,10 +615,25 @@ public function testInvalidValue(): void
$field = Checkbox::widget()->value(new stdClass());

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Checkbox widget requires a string, numeric, bool, Stringable or null value.');
$this->expectExceptionMessage('Checkbox widget requires a string, Stringable, numeric, bool or null value.');
$field->render();
}

public function testStringableValue(): void
{
$actualHtml = Checkbox::widget()
->inputValue('value')
->value(new StringableObject('value'))
->render();
$expectedHtml = <<<HTML
<div>
<input type="checkbox" value="value" checked>
</div>
HTML;

$this->assertSame($expectedHtml, $actualHtml);
}

public function testInvalidClassesWithCustomError(): void
{
$inputData = new InputData('company', '');
Expand Down
61 changes: 40 additions & 21 deletions tests/Field/RadioListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,35 +587,54 @@ public function testDisabled(): void
$this->assertSame($expected, $result);
}

public function testWithStringableValue(): void
{
$result = RadioList::widget()
->name('number')
->useContainer(false)
->hideLabel()
->items([1 => 'One', 2 => 'Two'])
->value(new StringableObject('2'))
->render();

$expected = <<<HTML
<div>
<label><input type="radio" name="number" value="1"> One</label>
<label><input type="radio" name="number" value="2" checked> Two</label>
</div>
HTML;

$this->assertSame($expected, $result);
}

public function testInvalidValue(): void
{
$field = RadioList::widget()->name('test')->value([]);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('"RadioList" field requires a string, numeric, bool, Stringable or null value.');
$this->expectExceptionMessage('"RadioList" field requires a string, Stringable, numeric, bool or null value.');
$field->render();
}

public static function dataValue(): array
{
return [
'bool' => [
true,
<<<HTML
<div>
<label><input type="radio" name="number" value="1" checked> One</label>
<label><input type="radio" name="number" value="2"> Two</label>
</div>
HTML,
],
'stringable' => [
new StringableObject('1'),
<<<HTML
<div>
<label><input type="radio" name="number" value="1" checked> One</label>
<label><input type="radio" name="number" value="2"> Two</label>
</div>
HTML,
],
];
}

#[DataProvider('dataValue')]
public function testValue(mixed $value, string $expectedHtml): void
{
$actualHtml = RadioList::widget()
->name('number')
->value($value)
->items([
'1' => 'One',
'2' => 'Two',
])
->useContainer(false)
->render();
$this->assertSame($expectedHtml, $actualHtml);
}

public function testWithoutName(): void
{
$field = RadioList::widget();
Expand Down
18 changes: 17 additions & 1 deletion tests/Field/SelectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Yiisoft\Form\PureField\InputData;
use Yiisoft\Form\Tests\Support\NullValidationRulesEnricher;
use Yiisoft\Form\Tests\Support\RequiredValidationRulesEnricher;
use Yiisoft\Form\Tests\Support\StringableObject;
use Yiisoft\Form\Tests\Support\StubValidationRulesEnricher;
use Yiisoft\Form\Theme\ThemeContainer;
use Yiisoft\Html\Tag\Optgroup;
Expand Down Expand Up @@ -659,7 +660,7 @@ public function testSingleInvalidValue(): void

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
'Non-multiple Select field requires a string, numeric, bool, Stringable or null value.'
'Non-multiple Select field requires a string, Stringable, numeric, bool or null value.'
);
$widget->render();
}
Expand All @@ -673,6 +674,21 @@ public function testMultipleInvalidValue(): void
$widget->render();
}

public function testStringableValue(): void
{
$actualHtml = Select::widget()
->optionsData(['1' => 'One'])
->value(new StringableObject('1'))
->useContainer(false)
->render();
$expectedHtml = <<<HTML
<select>
<option value="1" selected>One</option>
</select>
HTML;
$this->assertSame($expectedHtml, $actualHtml);
}

public function testEnrichFromValidationRulesEnabled(): void
{
$html = Select::widget()
Expand Down