Skip to content

Commit

Permalink
improving code coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
markhuot committed Nov 18, 2023
1 parent fb89802 commit 1a1f5c0
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 48 deletions.
1 change: 0 additions & 1 deletion .run/Pest.run.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Pest" type="PestRunConfigurationType">
<CommandLine parameters="-d pcov.enabled=0" />
<option name="pestRunnerSettings">
<PestRunner directory="$PROJECT_DIR$/tests" method="" />
</option>
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
],
"require-dev": {
"markhuot/craft-pest-core": "dev-sequence",
"markhuot/craft-pest-core": "dev-main",
"phpstan/phpstan": "^1.10",
"laravel/pint": "^1.13",
"craftcms/craft": "dev-main"
Expand Down
3 changes: 1 addition & 2 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
</php>
<source>
<include>
<directory suffix=".php">./modules</directory>
<directory suffix=".php">./templates</directory>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
32 changes: 30 additions & 2 deletions src/behaviors/BodyParamObjectBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ public function getBodyParamObject(string $class, string $formName = '')
return $this->handleData($data, $class, $formName);
}

/**
* @template T
*
* @param class-string<T> $class
* @return T
*/
public function getBodyParamObjectOrFail(string $class, string $formName = '')
{
if (! $this->owner->getIsPost()) {
throw new BadRequestHttpException('Post request required');
}

// Get the post data
$data = $this->owner->getBodyParams();

$data = $this->handleData($data, $class, $formName);
if ($data->errors) {
throw new \RuntimeException(implode(', ', $data->getErrorSummary(true)));
}

return $data;
}

/**
* @template T
*
Expand All @@ -56,7 +79,12 @@ public function getQueryParamObject(string $class, string $formName = '')

public function getQueryParamObjectOrFail(string $class, string $formName = '')
{
return $this->handleData($this->owner->getQueryParams(), $class, $formName, true, false);
$data = $this->handleData($this->owner->getQueryParams(), $class, $formName);
if ($data->errors) {
throw new \RuntimeException(implode(', ', $data->getErrorSummary(true)));
}

return $data;
}

/**
Expand Down Expand Up @@ -87,7 +115,7 @@ protected function handleData(array $data, string $class, string $formName = '',
// and render HTML or throw the exception if it's called ->withoutExceptionHandling, but that's
// not possible today so we're going to ignore it and come back to it later.
// @phpstan-ignore-next-line
if (function_exists('test') && test()->shouldSkipExceptionHandling() && ! empty($model->errors)) {
if (function_exists('test') && test()->shouldRenderExceptionsAsHtml() && ! empty($model->errors)) {
throw new \RuntimeException(collect($model->errors)->flatten()->join(' '));
}
} else {
Expand Down
6 changes: 6 additions & 0 deletions src/behaviors/RenderFieldHtmlBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace markhuot\keystone\behaviors;

use Craft;
use craft\base\ElementInterface;
use craft\base\FieldInterface;
use craft\fieldlayoutelements\CustomField;
use craft\web\View;
use yii\base\Behavior;

/**
Expand All @@ -14,10 +16,14 @@ class RenderFieldHtmlBehavior extends Behavior
{
public function getFieldHtml(FieldInterface $field)
{
$oldTemplateMode = Craft::$app->getView()->getTemplateMode();
Craft::$app->getView()->setTemplateMode(View::TEMPLATE_MODE_CP);

foreach ($this->owner->getFieldLayout()->createForm($this->owner)->tabs as $tab) {
foreach ($tab->elements as [$fieldLayout, $isConditional, $fieldHtml]) {
if ($fieldLayout instanceof CustomField) {
if ($fieldLayout->getField()->handle === $field->handle) {
Craft::$app->getView()->setTemplateMode($oldTemplateMode);
return $fieldHtml;
}
}
Expand Down
20 changes: 5 additions & 15 deletions src/controllers/ComponentsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ComponentsController extends Controller
{
public function actionAdd()
{
$data = $this->request->getQueryParamObject(AddComponentRequest::class);
$data = $this->request->getQueryParamObjectOrFail(AddComponentRequest::class);
$parent = (new GetParentFromPath)->handle($data->element->id, $data->field->id, $data->path);

return $this->asCpScreen()
Expand All @@ -43,7 +43,7 @@ public function actionAdd()

public function actionStore()
{
$data = $this->request->getBodyParamObject(StoreComponentRequest::class);
$data = $this->request->getBodyParamObjectOrFail(StoreComponentRequest::class);

(new AddComponent)->handle(
elementId: $data->element->id,
Expand Down Expand Up @@ -79,7 +79,7 @@ public function actionEdit()

public function actionUpdate()
{
$component = $this->request->getBodyParamObject(Component::class);
$component = $this->request->getBodyParamObjectOrFail(Component::class);
$fields = $this->request->getBodyParam('fields', []);

(new EditComponentData)->handle($component, $fields);
Expand All @@ -89,7 +89,7 @@ public function actionUpdate()

public function actionDelete()
{
$component = $this->request->getBodyParamObject(Component::class);
$component = $this->request->getBodyParamObjectOrFail(Component::class);
(new DeleteComponent)->handle($component);

return $this->asSuccess('Component deleted', [
Expand All @@ -99,21 +99,11 @@ public function actionDelete()

public function actionMove()
{
$data = $this->request->getBodyParamObject(MoveComponentRequest::class);
$data = $this->request->getBodyParamObjectOrFail(MoveComponentRequest::class);
(new MoveComponent)->handle($data->source, $data->position, $data->target, $data->slot);

return $this->asSuccess('Component moved', [
'fieldHtml' => $data->getTargetElement()->getFieldHtml($data->getTargetField()),
]);
}

public function actionGetEditModalHtml()
{
$id = $this->request->getRequiredBodyParam('id');
$component = Component::findOne(['id' => $id]);

return Craft::$app->getView()->renderTemplate('keystone/builder/edit', [
'component' => $component,
]);
}
}
4 changes: 4 additions & 0 deletions src/factories/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
use markhuot\keystone\models\ComponentData;
use SplObjectStorage;

/**
* @method self type(string $type)
* @method \markhuot\keystone\models\Component create(array $attributes = [])
*/
class Component extends Factory
{
public static $tests;
Expand Down
2 changes: 1 addition & 1 deletion src/models/http/AddComponentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use craft\base\ElementInterface;
use craft\base\FieldInterface;
use craft\base\Model;
use markhuot\keystone\base\Model;
use markhuot\keystone\validators\Required;
use markhuot\keystone\validators\Safe;

Expand Down
28 changes: 7 additions & 21 deletions src/models/http/MoveComponentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,29 @@
use Craft;
use craft\base\ElementInterface;
use craft\base\FieldInterface;
use craft\base\Model;
use markhuot\keystone\base\Model;
use markhuot\keystone\enums\MoveComponentPosition;
use markhuot\keystone\models\Component;
use markhuot\keystone\validators\Required;
use markhuot\keystone\validators\Safe;

use function markhuot\keystone\helpers\base\app;
use function markhuot\keystone\helpers\base\throw_if;

class MoveComponentRequest extends Model
{
#[Required]
public Component $source;

#[Required]
public Component $target;

#[Required]
public MoveComponentPosition $position;

#[Safe]
public ?string $slot = null;

/**
* @return array<string>
*/
public function safeAttributes(): array
{
return [...parent::safeAttributes(), 'slot'];
}

/**
* @return array<mixed>
*/
public function rules(): array
{
return [
['source', 'required'],
['target', 'required'],
['position', 'required'],
];
}

public function getTargetElement(): ElementInterface
{
// We ignore the next line for phpstan because Craft types on the second argument
Expand Down
105 changes: 100 additions & 5 deletions tests/RouteTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,102 @@
<?php

it('loads styles panel')
->skip()
->actingAsAdmin()
->get('/keystone/components/edit')
->assertOk();
use craft\helpers\UrlHelper;
use markhuot\craftpest\factories\Entry;
use markhuot\keystone\models\Component;

it('loads add panel', function () {
$component = Component::factory()->type('keystone/text')->create();

$this->actingAsAdmin()
->get(UrlHelper::actionUrl('keystone/components/add', [
'elementId' => $component->elementId,
'fieldId' => $component->fieldId,
'path' => $component->path,
'sortOrder' => 1,
]))
->assertOk();
});

it('loads edit panel', function ($type) {
$component = Component::factory()->type($type)->create();

$this->actingAsAdmin()
->get(UrlHelper::cpUrl('keystone/components/edit', $component->getQueryCondition()))
->assertOk();
})->with([
'keystone/asset', 'keystone/elementquery', 'keystone/fragment',
'keystone/icon', 'keystone/link', 'keystone/heading',
'keystone/section', 'keystone/text',
]);

it('stores a component', function () {
$component = Component::factory()
->type('keystone/section')
->elementId(Entry::factory()->section('pages')->create()->id)
->create();

$this->actingAsAdmin()
->postJson(UrlHelper::actionUrl('keystone/components/store'), [
'elementId' => $component->elementId,
'fieldId' => $component->fieldId,
'sortOrder' => 1,
'path' => $component->getChildPath(),
'type' => 'keystone/text',
])
->assertOk()
->assertJsonPath('message', 'Component added');
});

it('updates a component', function () {
$component = Component::factory()
->type('keystone/text')
->elementId(Entry::factory()->section('pages')->create()->id)
->create();
$component->data->merge(['text' => 'foo'])->save();

$this->actingAsAdmin()
->postJson(UrlHelper::actionUrl('keystone/components/update'), [
'id' => $component->id,
'elementId' => $component->elementId,
'fieldId' => $component->fieldId,
'fields' => ['text' => 'bar'],
])
->assertOk()
->assertJsonPath('message', 'Component saved');

$component->refresh();
expect($component->data)->get('text')->toBe('bar');
});

it('deletes a component', function () {
$component = Component::factory()
->type('keystone/text')
->elementId(Entry::factory()->section('pages')->create()->id)
->create();

$this->actingAsAdmin()
->postJson(UrlHelper::actionUrl('keystone/components/delete'), [
'id' => $component->id,
'elementId' => $component->elementId,
'fieldId' => $component->fieldId,
])
->assertOk()
->assertJsonPath('message', 'Component deleted');
});

it('moves a component', function () {
$components = Component::factory()
->type('keystone/section')
->elementId(Entry::factory()->section('pages')->create()->id)
->count(2)
->create();

$this->actingAsAdmin()
->postJson(UrlHelper::actionUrl('keystone/components/move'), [
'source' => $components[0]->getQueryCondition(),
'target' => $components[1]->getQueryCondition(),
'position' => \markhuot\keystone\enums\MoveComponentPosition::AFTER,
])
->assertOk()
->assertJsonPath('message', 'Component moved');
});

0 comments on commit 1a1f5c0

Please sign in to comment.