invoicemgmt-1.0.0/tests/src/Kernel/InvoiceMgmtHooksTest.php
tests/src/Kernel/InvoiceMgmtHooksTest.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\invoicemgmt\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\Entity\ParagraphsType;
/**
* Kernel tests for invoicemgmt module hooks.
*
* @group invoicemgmt
*/
class InvoiceMgmtHooksTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'user',
'field',
'text',
'node',
'paragraphs',
'entity_reference_revisions',
'file',
'invoicemgmt',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installEntitySchema('paragraph');
$this->installEntitySchema('file');
$this->installSchema('system', ['sequences']);
$this->installSchema('node', ['node_access']);
$this->installSchema('file', ['file_usage']);
$this->installConfig(['system', 'field', 'node', 'paragraphs']);
// Create content types.
$this->createInvoiceContentType();
$this->createClientContentType();
$this->createInvoiceItemParagraphType();
}
/**
* Tests hook_entity_presave for invoice number generation.
*
* @covers invoicemgmt_entity_presave
*/
public function testEntityPresaveInvoiceNumberGeneration(): void {
$invoice = Node::create([
'type' => 'invoice',
'title' => 'Test Invoice',
]);
// Invoice number should be empty before save.
$this->assertTrue($invoice->get('field_invoice_number')->isEmpty());
// Save the invoice to trigger hook_entity_presave.
$invoice->save();
// Invoice number should be generated after save.
$this->assertFalse($invoice->get('field_invoice_number')->isEmpty());
$invoice_number = $invoice->get('field_invoice_number')->value;
$this->assertMatchesRegularExpression('/^INV-\d{6}-\d{3}$/', $invoice_number);
}
/**
* Tests hook_entity_presave for grand total calculation.
*
* @covers invoicemgmt_entity_presave
*/
public function testEntityPresaveGrandTotalCalculation(): void {
// Create invoice items (paragraphs).
$item1 = Paragraph::create([
'type' => 'invoice_item',
'field_amount' => 100.50,
]);
$item1->save();
$item2 = Paragraph::create([
'type' => 'invoice_item',
'field_amount' => 200.25,
]);
$item2->save();
// Create invoice with items.
$invoice = Node::create([
'type' => 'invoice',
'title' => 'Test Invoice with Items',
'field_items' => [
['target_id' => $item1->id(), 'target_revision_id' => $item1->getRevisionId()],
['target_id' => $item2->id(), 'target_revision_id' => $item2->getRevisionId()],
],
]);
// Grand total should be empty before save.
$this->assertTrue($invoice->get('field_grand_total')->isEmpty());
// Save the invoice to trigger hook_entity_presave.
$invoice->save();
// Grand total should be calculated after save.
$this->assertFalse($invoice->get('field_grand_total')->isEmpty());
$grand_total = (float) $invoice->get('field_grand_total')->value;
$this->assertEquals(300.75, $grand_total);
}
/**
* Tests hook_entity_presave with empty invoice items.
*
* @covers invoicemgmt_entity_presave
*/
public function testEntityPresaveWithEmptyItems(): void {
$invoice = Node::create([
'type' => 'invoice',
'title' => 'Test Invoice without Items',
]);
$invoice->save();
// Grand total should be 0 when no items exist.
$grand_total = (float) $invoice->get('field_grand_total')->value;
$this->assertEquals(0.0, $grand_total);
}
/**
* Tests hook_entity_presave with items having no amount.
*
* @covers invoicemgmt_entity_presave
*/
public function testEntityPresaveWithItemsWithoutAmount(): void {
// Create invoice item without amount.
$item = Paragraph::create([
'type' => 'invoice_item',
]);
$item->save();
$invoice = Node::create([
'type' => 'invoice',
'title' => 'Test Invoice with Item without Amount',
'field_items' => [
['target_id' => $item->id(), 'target_revision_id' => $item->getRevisionId()],
],
]);
$invoice->save();
// Grand total should be 0 when items have no amount.
$grand_total = (float) $invoice->get('field_grand_total')->value;
$this->assertEquals(0.0, $grand_total);
}
/**
* Tests hook_entity_presave only affects invoice nodes.
*
* @covers invoicemgmt_entity_presave
*/
public function testEntityPresaveOnlyAffectsInvoices(): void {
$client = Node::create([
'type' => 'client',
'title' => 'Test Client',
]);
// Should not throw any errors for non-invoice nodes.
$this->expectNotToPerformAssertions();
$client->save();
}
/**
* Tests hook_form_alter for invoice forms.
*
* @covers invoicemgmt_form_alter
*/
public function testFormAlterInvoiceForm(): void {
$form = [];
$form_state = new FormState();
// Add fields that should be altered.
$form['field_invoice_number'] = [
'widget' => [
0 => [
'value' => ['#attributes' => []],
],
],
];
$form['field_grand_total'] = [
'widget' => [
0 => [
'value' => ['#attributes' => []],
],
],
];
$form['field_client'] = [
'widget' => [
0 => [],
],
];
// Call the form alter hook.
invoicemgmt_form_alter($form, $form_state, 'node_invoice_form');
// Check that invoice number field is disabled.
$this->assertTrue($form['field_invoice_number']['#disabled']);
$this->assertEquals('readonly', $form['field_invoice_number']['widget'][0]['value']['#attributes']['readonly']);
$this->assertStringContainsString('automatically generated', (string) $form['field_invoice_number']['#description']);
// Check that grand total field is disabled.
$this->assertTrue($form['field_grand_total']['#disabled']);
$this->assertEquals('readonly', $form['field_grand_total']['widget'][0]['value']['#attributes']['readonly']);
$this->assertStringContainsString('automatically calculated', (string) $form['field_grand_total']['#description']);
// Check that client field has add client link.
$this->assertArrayHasKey('#suffix', $form['field_client']['widget'][0]);
}
/**
* Tests hook_form_alter for invoice edit form.
*
* @covers invoicemgmt_form_alter
*/
public function testFormAlterInvoiceEditForm(): void {
$form = [];
$form_state = new FormState();
$form['field_invoice_number'] = [
'widget' => [
0 => [
'value' => ['#attributes' => []],
],
],
];
invoicemgmt_form_alter($form, $form_state, 'node_invoice_edit_form');
$this->assertTrue($form['field_invoice_number']['#disabled']);
}
/**
* Tests hook_form_alter doesn't affect other forms.
*
* @covers invoicemgmt_form_alter
*/
public function testFormAlterDoesNotAffectOtherForms(): void {
$form = [
'field_invoice_number' => [
'widget' => [
0 => [
'value' => ['#attributes' => []],
],
],
],
];
$form_state = new FormState();
invoicemgmt_form_alter($form, $form_state, 'node_article_form');
// Field should not be disabled for non-invoice forms.
$this->assertArrayNotHasKey('#disabled', $form['field_invoice_number']);
}
/**
* Tests hook_theme implementation.
*
* @covers invoicemgmt_theme
*/
public function testThemeHook(): void {
$theme_info = invoicemgmt_theme([], '', '', '');
$this->assertArrayHasKey('node__invoice__full', $theme_info);
$this->assertEquals('node--invoice--full', $theme_info['node__invoice__full']['template']);
$this->assertEquals('node', $theme_info['node__invoice__full']['base hook']);
$this->assertArrayHasKey('entity_print__invoice', $theme_info);
$this->assertEquals('entity-print--invoice', $theme_info['entity_print__invoice']['template']);
$this->assertArrayHasKey('variables', $theme_info['entity_print__invoice']);
}
/**
* Creates invoice content type for testing.
*/
protected function createInvoiceContentType(): void {
$invoice_type = NodeType::create([
'type' => 'invoice',
'name' => 'Invoice',
]);
$invoice_type->save();
// Create required fields.
$this->createTextField('node', 'invoice', 'field_invoice_number', 'Invoice Number', 1);
$this->createDecimalField('node', 'invoice', 'field_grand_total', 'Grand Total', 1);
$this->createEntityReferenceRevisionsField('node', 'invoice', 'field_items', 'Invoice Items', 'paragraph', -1);
$this->createEntityReferenceField('node', 'invoice', 'field_client', 'Client', 'node', 1, ['client']);
}
/**
* Creates client content type for testing.
*/
protected function createClientContentType(): void {
$client_type = NodeType::create([
'type' => 'client',
'name' => 'Client',
]);
$client_type->save();
}
/**
* Creates invoice item paragraph type for testing.
*/
protected function createInvoiceItemParagraphType(): void {
$paragraph_type = ParagraphsType::create([
'id' => 'invoice_item',
'label' => 'Invoice Item',
]);
$paragraph_type->save();
$this->createDecimalField('paragraph', 'invoice_item', 'field_amount', 'Amount', 1);
}
/**
* Helper method to create text fields.
*/
protected function createTextField(string $entity_type, string $bundle, string $field_name, string $label, int $cardinality): void {
$field_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'string',
'cardinality' => $cardinality,
]);
$field_storage->save();
$field_config = \Drupal::entityTypeManager()->getStorage('field_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
]);
$field_config->save();
}
/**
* Helper method to create decimal fields.
*/
protected function createDecimalField(string $entity_type, string $bundle, string $field_name, string $label, int $cardinality): void {
$field_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'decimal',
'cardinality' => $cardinality,
'settings' => [
'precision' => 10,
'scale' => 2,
],
]);
$field_storage->save();
$field_config = \Drupal::entityTypeManager()->getStorage('field_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
]);
$field_config->save();
}
/**
* Helper method to create entity reference revisions fields.
*/
protected function createEntityReferenceRevisionsField(string $entity_type, string $bundle, string $field_name, string $label, string $target_type, int $cardinality): void {
$field_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'entity_reference_revisions',
'cardinality' => $cardinality,
'settings' => [
'target_type' => $target_type,
],
]);
$field_storage->save();
$field_config = \Drupal::entityTypeManager()->getStorage('field_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
'settings' => [
'handler' => 'default:' . $target_type,
'handler_settings' => [
'target_bundles' => ['invoice_item'],
],
],
]);
$field_config->save();
}
/**
* Helper method to create entity reference fields.
*/
protected function createEntityReferenceField(string $entity_type, string $bundle, string $field_name, string $label, string $target_type, int $cardinality, array $target_bundles): void {
$field_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'entity_reference',
'cardinality' => $cardinality,
'settings' => [
'target_type' => $target_type,
],
]);
$field_storage->save();
$field_config = \Drupal::entityTypeManager()->getStorage('field_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
'settings' => [
'handler' => 'default:' . $target_type,
'handler_settings' => [
'target_bundles' => array_combine($target_bundles, $target_bundles),
],
],
]);
$field_config->save();
}
}