image_to_media_swapper-2.x-dev/tests/src/Functional/SwapperControllerRecheckTest.php
tests/src/Functional/SwapperControllerRecheckTest.php
<?php
namespace Drupal\Tests\image_to_media_swapper\Functional;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\Core\File\FileExists;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\image_to_media_swapper\Traits\MediaFieldSetupTrait;
use Drupal\image_to_media_swapper\Entity\MediaSwapRecord;
use Drupal\node\Entity\Node;
/**
* Tests the MediaSwapRecord recheck functionality in SwapperController.
*
* @group image_to_media_swapper
*/
class SwapperControllerRecheckTest extends BrowserTestBase {
use MediaFieldSetupTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'node',
'media',
'file',
'image',
'options',
'filter',
'editor',
'field',
'user',
'text',
'ckeditor5',
'linkit',
'image_to_media_swapper',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* A user with permission to access the recheck functionality.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A user without permission to access the recheck functionality.
*
* @var \Drupal\user\UserInterface
*/
protected $regularUser;
/**
* The media swap record to be rechecked.
*
* @var \Drupal\image_to_media_swapper\Entity\MediaSwapRecord
*/
protected $mediaSwapRecord;
/**
* The node with image content to be processed.
*
* @var \Drupal\node\NodeInterface
*/
protected $nodeWithImage;
/**
* The file URL generator service.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// In BrowserTestBase, entity schemas are installed automatically
// by the profile, so we don't need to install them manually.
// Just ensure we've got our file usage schema.
$this->container->get('config.installer')->installDefaultConfig('module', 'file');
// Get the file URL generator service.
$this->fileUrlGenerator = $this->container->get('file_url_generator');
// Set up test content types and fields using the trait.
$this->createNodeTypeWithBody();
$this->createMediaImageType();
$this->createFilterFormatWithMediaEmbed();
// Create users with different permission levels.
$this->adminUser = $this->drupalCreateUser([
'access batch media swapper',
'administer nodes',
]);
$this->regularUser = $this->drupalCreateUser([
'administer nodes',
]);
// Create a node with an image in a text field.
$this->nodeWithImage = $this->createNodeWithImageInTextField();
// Create a pending MediaSwapRecord for the node.
$this->mediaSwapRecord = $this->createPendingMediaSwapRecord($this->nodeWithImage);
}
/**
* Tests that a user with appropriate permissions can recheck a record.
*/
public function testRecheckRecordWithPermission(): void {
// Login as admin user who has proper permissions.
$this->drupalLogin($this->adminUser);
// Access the recheck URL.
$url = Url::fromRoute('image_to_media_swapper.recheck_record', [
'media_swap_record' => $this->mediaSwapRecord->id(),
]);
$this->drupalGet($url);
// Verify response.
$this->assertSession()->statusCodeEquals(200);
// Verify status message appears.
$this->assertSession()->pageTextContains('The record has been rechecked');
// Reload the record from storage to check if its status changed.
$updated_record = MediaSwapRecord::load($this->mediaSwapRecord->id());
// The status should still be 'pending': we didn't modify the test node.
$this->assertEquals('pending', $updated_record->getProcessingStatus());
// Now update the node to remove the image, then recheck again.
$this->updateNodeToRemoveImage($this->nodeWithImage);
// Revisit the recheck URL.
$this->drupalGet($url);
// Reload the record again - use static load method instead of service.
$updated_record = MediaSwapRecord::load($this->mediaSwapRecord->id());
// Now the status should be 'completed' since the image was removed.
$this->assertEquals('completed', $updated_record->getProcessingStatus());
}
/**
* Tests that a user without appropriate permissions cannot recheck a record.
*/
public function testRecheckRecordWithoutPermission(): void {
// Login as regular user without proper permissions.
$this->drupalLogin($this->regularUser);
// Try to access the recheck URL.
$url = Url::fromRoute('image_to_media_swapper.recheck_record', [
'media_swap_record' => $this->mediaSwapRecord->id(),
]);
$this->drupalGet($url);
// Verify access is denied.
$this->assertSession()->statusCodeEquals(403);
}
/**
* Creates a node with an image tag in a text field.
*
* @return \Drupal\node\NodeInterface
* The created node.
*/
protected function createNodeWithImageInTextField(): Node {
// We'll use the article content type created with body field in setup.
// Create a file in the public files directory.
$file = $this->createTestFile('example.jpg');
// Generate URL using the file URL generator service.
$file_url = $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri());
// Create a node with an img tag in the body field.
$node = Node::create([
'type' => 'article',
'title' => 'Test Page with Image',
'body' => [
'value' => '<p>Test content with an image: <img src="' . $file_url . '" alt="Test image" /></p>',
'format' => 'full_html',
],
'uid' => $this->adminUser->id(),
]);
$node->save();
return $node;
}
/**
* Creates a test file.
*
* @param string $filename
* The filename.
*
* @return \Drupal\file\FileInterface
* The created file entity.
*/
protected function createTestFile($filename) {
$file_system = $this->container->get('file_system');
// Ensure the directory exists.
$directory = 'public://';
$file_system->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
// Define the target URI for the file.
$uri = 'public://' . $filename;
// Get the module extension path using the modern approach.
$module_path = $this->container->get('extension.list.module')->getPath('image_to_media_swapper');
$source = $module_path . '/tests/fixtures/test-image.png';
if (!file_exists($source)) {
// Fallback to core test image if module test image doesn't exist.
$source = DRUPAL_ROOT . '/core/misc/druplicon.png';
}
$file_system->copy($source, $uri, FileExists::Replace);
// Create the file entity.
$file = File::create([
'uri' => $uri,
'filename' => $filename,
'status' => FileInterface::STATUS_PERMANENT,
]);
$file->save();
return $file;
}
/**
* Creates a pending MediaSwapRecord entity for the given node.
*
* @param \Drupal\node\NodeInterface $node
* The node to create a record for.
*
* @return \Drupal\image_to_media_swapper\Entity\MediaSwapRecord
* The created MediaSwapRecord.
*/
protected function createPendingMediaSwapRecord($node): MediaSwapRecord {
// Create a MediaSwapRecord entity.
$media_swap_record = MediaSwapRecord::create([
'field_selector' => 'node.article.body',
'target_entity_type' => 'node',
'target_bundle' => 'article',
'target_entity_id' => $node->id(),
'batch_category' => 'images',
'processing_status' => 'pending',
'processed_time' => time(),
'uid' => $this->adminUser->id(),
]);
$media_swap_record->save();
return $media_swap_record;
}
/**
* Updates a node to remove image tags from its content.
*
* @param \Drupal\node\NodeInterface $node
* The node to update.
*/
protected function updateNodeToRemoveImage($node): void {
// Update the node to remove the image tag.
$node->set('body', [
'value' => '<p>Test content with no image.</p>',
'format' => 'full_html',
]);
$node->save();
}
}
