first commit

This commit is contained in:
2020-06-08 23:57:36 +02:00
commit 6277454f7a
16057 changed files with 1715382 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="alternate" href="photo_flickr.json"
type="application/json+oembed" title="Druplicon FTW!">
</head>
<body></body>
</html>
@@ -0,0 +1,12 @@
{
"type": "photo",
"title": "Druplicon FTW!",
"width": "88",
"height": "100",
"url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_width": 88,
"thumbnail_height": 100,
"provider_name": "Flickr",
"version": "1.0"
}
@@ -0,0 +1,10 @@
{
"type": "photo",
"title": "Druplicon FTW!",
"url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_width": 88,
"thumbnail_height": 100,
"provider_name": "Flickr",
"version": "1.0"
}
@@ -0,0 +1,76 @@
[
{
"provider_name": "Vimeo",
"provider_url": "https:\/\/vimeo.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/vimeo.com\/*",
"https:\/\/vimeo.com\/album\/*\/video\/*",
"https:\/\/vimeo.com\/channels\/*\/*",
"https:\/\/vimeo.com\/groups\/*\/videos\/*",
"https:\/\/vimeo.com\/ondemand\/*\/*",
"https:\/\/player.vimeo.com\/video\/*"
],
"url": "https:\/\/vimeo.com\/api\/oembed.{format}",
"discovery": true
}
]
},
{
"provider_name": "Twitter",
"provider_url": "http:\/\/www.twitter.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/twitter.com\/*\/status\/*",
"https:\/\/*.twitter.com\/*\/status\/*"
],
"url": "https:\/\/publish.twitter.com\/oembed"
}
]
},
{
"provider_name": "CollegeHumor",
"provider_url": "http:\/\/www.collegehumor.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/www.collegehumor.com\/video\/*"
],
"url": "http:\/\/www.collegehumor.com\/oembed.{format}",
"discovery": true
}
]
},
{
"provider_name": "Flickr",
"provider_url": "http:\/\/www.flickr.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/*.flickr.com\/photos\/*",
"http:\/\/flic.kr\/p\/*"
],
"url": "http:\/\/www.flickr.com\/services\/oembed\/",
"discovery": true
}
]
},
{
"provider_name": "YouTube",
"provider_url": "https://www.youtube.com/",
"endpoints": [
{
"schemes": [
"https://*.youtube.com/watch*",
"https://*.youtube.com/v/*\"",
"https://youtu.be/*"
],
"url": "https://www.youtube.com/oembed",
"discovery": true
}
]
}
]
@@ -0,0 +1,13 @@
{
"url": "https:\/\/twitter.com\/drupaldevdays\/status\/935643039741202432",
"author_name": "Drupal Dev Days",
"author_url": "https:\/\/twitter.com\/drupaldevdays",
"html": "<h1>By the power of Greyskull, Twitter works!</h1>",
"width": 550,
"height": 360,
"type": "rich",
"cache_age": "3153600000",
"provider_name": "Twitter",
"provider_url": "https:\/\/twitter.com",
"version": "1.0"
}
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="alternate" href="video_collegehumor.xml"
type="application/xml+oembed" title="Let&#039;s Not Get a Drink Sometime">
</head>
<body></body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<oembed>
<type>video</type>
<version>1.0</version>
<title>Let's Not Get a Drink Sometime</title>
<https/>
<author_name>CollegeHumor</author_name>
<author_url>http://www.collegehumor.com</author_url>
<provider_name>CollegeHumor</provider_name>
<provider_url>http://www.collegehumor.com</provider_url>
<width>610</width>
<height>343</height>
<html><h1>By the power of Greyskull, CollegeHumor works!</h1>
</html>
<thumbnail_url>internal:/core/misc/druplicon.png</thumbnail_url>
<thumbnail_width>88</thumbnail_width>
<thumbnail_height>100</thumbnail_height>
</oembed>
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="alternate" href="video_vimeo-no-title.json"
type="application/json+oembed" title="">
</head>
<body></body>
</html>
@@ -0,0 +1,16 @@
{
"type": "video",
"version": "1.0",
"provider_name": "Vimeo",
"provider_url": "https:\/\/vimeo.com\/",
"title": "",
"author_name": "Tendenci - The Open Source AMS",
"author_url": "https:\/\/vimeo.com\/schipul",
"html": "<iframe width=\"480\">By the power of Greyskull, Vimeo works!</iframe>",
"width": 480,
"height": 360,
"description": "Special thanks to Tendenci, formerly Schipul for sponsoring this video with training, equipment and time. The open source way. All creative however was self directed by the individuals - A. Hughes (www.schipul.com\/ahughes) featuring QCait (www.schipul.com\/qcait) - Hands On Drupal\n\nDrupal is a free software package that allows an individual or a community of users to easily publish, manage and organize a wide variety of content on a website.\n\nNeed a little Drupal help or just want to geek out with us? Visit our www.schipul.com\/drupal for more info - we'd love to connect!\n\nGo here for Drupal Common Terms and Suggested Modules : http:\/\/schipul.com\/en\/helpfiles\/v\/229",
"thumbnail_url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_width": 295,
"thumbnail_height": 221
}
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="alternate" href="video_vimeo.json"
type="application/json+oembed" title="Drupal Rap Video - Schipulcon09">
</head>
<body></body>
</html>
@@ -0,0 +1,16 @@
{
"type": "video",
"version": "1.0",
"provider_name": "Vimeo",
"provider_url": "https:\/\/vimeo.com\/",
"title": "Drupal Rap Video - Schipulcon09",
"author_name": "Tendenci - The Open Source AMS",
"author_url": "https:\/\/vimeo.com\/schipul",
"html": "<iframe width=\"480\">By the power of Greyskull, Vimeo works!</iframe>",
"width": 480,
"height": 360,
"description": "Special thanks to Tendenci, formerly Schipul for sponsoring this video with training, equipment and time. The open source way. All creative however was self directed by the individuals - A. Hughes (www.schipul.com\/ahughes) featuring QCait (www.schipul.com\/qcait) - Hands On Drupal\n\nDrupal is a free software package that allows an individual or a community of users to easily publish, manage and organize a wide variety of content on a website.\n\nNeed a little Drupal help or just want to geek out with us? Visit our www.schipul.com\/drupal for more info - we'd love to connect!\n\nGo here for Drupal Common Terms and Suggested Modules : http:\/\/schipul.com\/en\/helpfiles\/v\/229",
"thumbnail_url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_width": 295,
"thumbnail_height": 221
}
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<link rel="alternate" href="video_youtube.json"
type="application/json+oembed" title="Everyday I'm Drupalin' Drupal Rap (Rick Ross - Hustlin)">
</head>
<body></body>
</html>
@@ -0,0 +1,15 @@
{
"type": "video",
"version": "1.0",
"provider_name": "YouTube",
"provider_url": "https:\/\/www.youtube.com\/",
"title": "Everyday I'm Drupalin' Drupal Rap (Rick Ross - Hustlin)",
"author_name": "Mirakolous",
"author_url": "https:\/\/www.youtube.com\/user\/Mirakolous",
"html": "<h1>By the power of Greyskull, YouTube works!</h1>",
"width": 480,
"height": 270,
"thumbnail_url": "internal:\/core\/misc\/druplicon.png",
"thumbnail_width": 480,
"thumbnail_height": 360
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
<?php
// @codingStandardsIgnoreFile
/**
* @file
* Contains database additions to drupal-8.4.0.bare.standard.php.gz for testing
* the upgrade path of https://www.drupal.org/node/2862422.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$role = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'user.role.authenticated')
->execute()
->fetchField();
$role = unserialize($role);
$role['permissions'][] = 'update media';
$role['permissions'][] = 'update any media';
$role['permissions'][] = 'delete media';
$role['permissions'][] = 'delete any media';
$role['permissions'][] = 'create media';
$connection->update('config')
->fields([
'data' => serialize($role),
'collection' => '',
'name' => 'user.role.authenticated',
])
->condition('collection', '')
->condition('name', 'user.role.authenticated')
->execute();
@@ -0,0 +1,8 @@
name: Media CKEditor plugin test
description: 'Provides functionality to test the Media Embed CKEditor integration.'
type: module
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:media
@@ -0,0 +1,24 @@
<?php
/**
* @file
* Helper module for the Media Embed CKEditor plugin tests.
*/
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
/**
* Implements hook_entity_view_alter().
*/
function media_test_ckeditor_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
// @see \Drupal\Tests\media\FunctionalJavascript\CKEditorIntegrationTest::testPreviewUsesDefaultThemeAndIsClientCacheable()
$build['#attributes']['data-media-embed-test-active-theme'] = \Drupal::theme()->getActiveTheme()->getName();
}
/**
* Implements hook_preprocess_HOOK().
*/
function media_test_ckeditor_preprocess_media_embed_error(&$variables) {
$variables['attributes']['class'][] = 'this-error-message-is-themeable';
}
@@ -0,0 +1,5 @@
services:
media_test_ckeditor.route_subscriber:
class: Drupal\media_test_ckeditor\Routing\RouteSubscriber
tags:
- { name: event_subscriber }
@@ -0,0 +1,25 @@
<?php
namespace Drupal\media_test_ckeditor\Controller;
use Drupal\filter\FilterFormatInterface;
use Drupal\media\Controller\MediaFilterController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Controller to allow testing of error handling in drupalmedia plugin.js.
*/
class TestMediaFilterController extends MediaFilterController {
/**
* {@inheritdoc}
*/
public function preview(Request $request, FilterFormatInterface $filter_format) {
if (\Drupal::state()->get('test_media_filter_controller_throw_error', FALSE)) {
throw new NotFoundHttpException();
}
return parent::preview($request, $filter_format);
}
}
@@ -0,0 +1,22 @@
<?php
namespace Drupal\media_test_ckeditor\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Listens to the dynamic route events.
*/
class RouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
public function alterRoutes(RouteCollection $collection) {
if ($route = $collection->get('media.filter.preview')) {
$route->setDefault('_controller', '\Drupal\media_test_ckeditor\Controller\TestMediaFilterController::preview');
}
}
}
@@ -0,0 +1,8 @@
name: Media Filter test
description: 'Provides functionality to test the Media Embed filter.'
type: module
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:media
@@ -0,0 +1,32 @@
<?php
/**
* @file
* Helper module for the Media Embed filter tests.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Implements hook_entity_access().
*/
function media_test_filter_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
return AccessResult::neutral()->addCacheTags(['_media_test_filter_access:' . $entity->getEntityTypeId() . ':' . $entity->id()]);
}
/**
* Implements hook_entity_view_alter().
*/
function media_test_filter_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
$build['#attributes']['data-media-embed-test-view-mode'] = $display->getMode();
}
/**
* Implements hook_preprocess_HOOK().
*/
function media_test_filter_preprocess_media_embed_error(&$variables) {
$variables['attributes']['class'][] = 'this-error-message-is-themeable';
}
@@ -0,0 +1,8 @@
name: Media oEmbed test
description: 'Provides functionality to mimic an oEmbed provider.'
type: module
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:media
@@ -0,0 +1,17 @@
<?php
/**
* @file
* Helper module for the Media oEmbed tests.
*/
use Drupal\media\OEmbed\Provider;
/**
* Implements hook_oembed_resource_url_alter().
*/
function media_test_oembed_oembed_resource_url_alter(array &$parsed_url, Provider $provider) {
if ($provider->getName() === 'Vimeo') {
$parsed_url['query']['altered'] = 1;
}
}
@@ -0,0 +1,6 @@
media_test_oembed.resource.get:
path: '/media_test_oembed/resource'
defaults:
_controller: '\Drupal\media_test_oembed\Controller\ResourceController::get'
requirements:
_access: 'TRUE'
@@ -0,0 +1,65 @@
<?php
namespace Drupal\media_test_oembed\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Test controller returning oEmbed resources from Media's test fixtures.
*/
class ResourceController {
/**
* Creates an oEmbed resource response.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @return \Symfony\Component\HttpFoundation\Response
* The oEmbed resource response.
*/
public function get(Request $request) {
$asset_url = $request->query->get('url');
$resources = \Drupal::state()->get(static::class, []);
if ($resources[$asset_url] === 404) {
$response = new Response('Not Found', 404);
}
else {
$content = file_get_contents($resources[$asset_url]);
$response = new Response($content);
$response->headers->set('Content-Type', 'application/json');
}
return $response;
}
/**
* Maps an asset URL to a local fixture representing its oEmbed resource.
*
* @param string $asset_url
* The asset URL.
* @param string $resource_path
* The path of the oEmbed resource representing the asset.
*/
public static function setResourceUrl($asset_url, $resource_path) {
$resources = \Drupal::state()->get(static::class, []);
$resources[$asset_url] = $resource_path;
\Drupal::state()->set(static::class, $resources);
}
/**
* Maps an asset URL to a 404 response.
*
* @param string $asset_url
* The asset URL.
*/
public static function setResource404($asset_url) {
$resources = \Drupal::state()->get(static::class, []);
$resources[$asset_url] = 404;
\Drupal::state()->set(static::class, $resources);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Drupal\media_test_oembed;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
/**
* Replaces oEmbed-related media services with testing versions.
*/
class MediaTestOembedServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
parent::alter($container);
$container->getDefinition('media.oembed.provider_repository')
->setClass(ProviderRepository::class);
$container->getDefinition('media.oembed.url_resolver')
->setClass(UrlResolver::class);
}
}
@@ -0,0 +1,55 @@
<?php
namespace Drupal\media_test_oembed;
use Drupal\media\OEmbed\Provider;
use Drupal\media\OEmbed\ProviderRepository as BaseProviderRepository;
/**
* Overrides the oEmbed provider repository service for testing purposes.
*
* This service does not use caching at all, and will always try to retrieve
* provider data from state before calling the parent methods.
*/
class ProviderRepository extends BaseProviderRepository {
/**
* {@inheritdoc}
*/
protected function cacheGet($cid) {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getAll() {
return \Drupal::state()->get(static::class) ?: parent::getAll();
}
/**
* {@inheritdoc}
*/
public function get($provider_name) {
$providers = \Drupal::state()->get(static::class, []);
if (isset($providers[$provider_name])) {
return $providers[$provider_name];
}
return parent::get($provider_name);
}
/**
* Stores an oEmbed provider value object in state.
*
* @param \Drupal\media\OEmbed\Provider $provider
* The provider to store.
*/
public function setProvider(Provider $provider) {
$providers = \Drupal::state()->get(static::class, []);
$name = $provider->getName();
$providers[$name] = $provider;
\Drupal::state()->set(static::class, $providers);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\media_test_oembed;
use Drupal\media\OEmbed\UrlResolver as BaseUrlResolver;
/**
* Overrides the oEmbed URL resolver service for testing purposes.
*/
class UrlResolver extends BaseUrlResolver {
/**
* Sets the endpoint URL for an oEmbed resource URL.
*
* @param string $url
* The resource URL.
* @param string $endpoint_url
* The endpoint URL.
*/
public static function setEndpointUrl($url, $endpoint_url) {
$urls = \Drupal::state()->get(static::class, []);
$urls[$url] = $endpoint_url;
\Drupal::state()->set(static::class, $urls);
}
/**
* {@inheritdoc}
*/
public function getResourceUrl($url, $max_width = NULL, $max_height = NULL) {
$urls = \Drupal::state()->get(static::class, []);
if (isset($urls[$url])) {
return $urls[$url];
}
return parent::getResourceUrl($url, $max_width, $max_height);
}
}
@@ -0,0 +1,23 @@
media.source.test:
type: media.source.field_aware
label: 'Test media source configuration'
mapping:
test_config_value:
type: string
label: 'Test config value'
media.source.test_translation:
type: media.source.test
label: 'Test media source with translations'
media.source.test_constraints:
type: media.source.test
label: 'Test media source with constraints configuration'
media.source.test_hidden_source_field:
type: media.source.test
label: 'Test media source with hidden source field'
media.source.test_different_displays:
type: media.source.test
label: 'Test media source with different source field displays'
@@ -0,0 +1,6 @@
name: 'Test media source'
type: module
description: 'Provides test media source to test configuration forms.'
core: 8.x
package: Testing
version: VERSION
@@ -0,0 +1,25 @@
<?php
namespace Drupal\media_test_source\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* A media test constraint.
*
* @Constraint(
* id = "MediaTestConstraint",
* label = @Translation("Media constraint for test purposes.", context = "Validation"),
* type = { "entity", "string" }
* )
*/
class MediaTestConstraint extends Constraint {
/**
* The default violation message.
*
* @var string
*/
public $message = 'Inappropriate text.';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\media_test_source\Plugin\Validation\Constraint;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the MediaTestConstraint.
*/
class MediaTestConstraintValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
if ($value instanceof EntityInterface) {
$string_to_test = $value->label();
}
elseif ($value instanceof FieldItemListInterface) {
$string_to_test = $value->value;
}
else {
return;
}
if (strpos($string_to_test, 'love Drupal') === FALSE) {
$this->context->addViolation($constraint->message);
}
}
}
@@ -0,0 +1,88 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\media\MediaInterface;
use Drupal\media\MediaSourceBase;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test",
* label = @Translation("Test source"),
* description = @Translation("Test media source."),
* allowed_field_types = {"string"},
* )
*/
class Test extends MediaSourceBase {
/**
* {@inheritdoc}
*/
public function getMetadataAttributes() {
// The metadata attributes are kept in state storage. This allows tests to
// change the metadata attributes and makes it easier to test different
// variations.
$attributes = \Drupal::state()->get('media_source_test_attributes', [
'attribute_1' => ['label' => $this->t('Attribute 1'), 'value' => 'Value 1'],
'attribute_2' => ['label' => $this->t('Attribute 2'), 'value' => 'Value 1'],
]);
return array_map(function ($item) {
return $item['label'];
}, $attributes);
}
/**
* {@inheritdoc}
*/
public function getMetadata(MediaInterface $media, $attribute_name) {
$attributes = \Drupal::state()->get('media_source_test_attributes', [
'attribute_1' => ['label' => $this->t('Attribute 1'), 'value' => 'Value 1'],
'attribute_2' => ['label' => $this->t('Attribute 2'), 'value' => 'Value 1'],
]);
if (in_array($attribute_name, array_keys($attributes))) {
return $attributes[$attribute_name]['value'];
}
return parent::getMetadata($media, $attribute_name);
}
/**
* {@inheritdoc}
*/
public function getPluginDefinition() {
return NestedArray::mergeDeep(
parent::getPluginDefinition(),
\Drupal::state()->get('media_source_test_definition', [])
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'test_config_value' => 'This is default value.',
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['test_config_value'] = [
'#type' => 'textfield',
'#title' => $this->t('Test config value'),
'#default_value' => $this->configuration['test_config_value'],
];
return $form;
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test_different_displays",
* label = @Translation("Test source with different displays"),
* description = @Translation("Test source with different displays."),
* allowed_field_types = {"entity_reference"},
* )
*/
class TestDifferentDisplays extends Test {
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
parent::prepareViewDisplay($type, $display);
$source_name = $this->getSourceFieldDefinition($type)->getName();
$source_component = $display->getComponent($source_name) ?: [];
$source_component['type'] = 'entity_reference_entity_id';
$display->setComponent($source_name, $source_component);
}
/**
* {@inheritdoc}
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
parent::prepareFormDisplay($type, $display);
$source_name = $this->getSourceFieldDefinition($type)->getName();
$source_component = $display->getComponent($source_name) ?: [];
$source_component['type'] = 'entity_reference_autocomplete_tags';
$display->setComponent($source_name, $source_component);
}
/**
* {@inheritdoc}
*/
protected function getSourceFieldName() {
return 'field_media_different_display';
}
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\media\MediaInterface;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test_translation",
* label = @Translation("Test source with translations"),
* description = @Translation("Test media source with translations."),
* allowed_field_types = {"string"},
* thumbnail_alt_metadata_attribute = "test_thumbnail_alt",
* )
*/
class TestTranslation extends Test {
/**
* {@inheritdoc}
*/
public function getMetadata(MediaInterface $media, $attribute_name) {
if ($attribute_name == 'thumbnail_uri') {
return 'public://' . $media->language()->getId() . '.png';
}
if ($attribute_name == 'test_thumbnail_alt') {
$langcode = $media->language()->getId();
return $this->t('Test Thumbnail @language', ['@language' => $langcode], ['langcode' => $langcode]);
}
return parent::getMetadata($media, $attribute_name);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\media\MediaSourceEntityConstraintsInterface;
use Drupal\media\MediaSourceFieldConstraintsInterface;
/**
* Provides generic media type.
*
* @MediaSource(
* id = "test_constraints",
* label = @Translation("Test source with constraints"),
* description = @Translation("Test media source that provides constraints."),
* allowed_field_types = {"string_long"},
* )
*/
class TestWithConstraints extends Test implements MediaSourceEntityConstraintsInterface, MediaSourceFieldConstraintsInterface {
/**
* {@inheritdoc}
*/
public function getEntityConstraints() {
return \Drupal::state()->get('media_source_test_entity_constraints', []);
}
/**
* {@inheritdoc}
*/
public function getSourceFieldConstraints() {
return \Drupal::state()->get('media_source_test_field_constraints', []);
}
}
@@ -0,0 +1,42 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test_hidden_source_field",
* label = @Translation("Test source with hidden source field"),
* description = @Translation("Test media source with hidden source field."),
* allowed_field_types = {"string"},
* )
*/
class TestWithHiddenSourceField extends Test {
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
$display->removeComponent($this->getSourceFieldDefinition($type)->getName());
}
/**
* {@inheritdoc}
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
$display->removeComponent($this->getSourceFieldDefinition($type)->getName());
}
/**
* {@inheritdoc}
*/
protected function getSourceFieldName() {
return 'field_media_hidden';
}
}
@@ -0,0 +1,11 @@
id: test
label: 'Test type'
description: 'Test type.'
source: test
source_configuration:
test_config_value: 'Kakec'
status: true
langcode: en
dependencies: { }
field_map:
metadata_attribute: 'field_attribute_config_test'
@@ -0,0 +1,9 @@
name: 'Media test type'
type: module
description: 'Provides test type for a media item.'
core: 8.x
package: Testing
version: VERSION
dependencies:
- drupal:media
- drupal:media_test_source
@@ -0,0 +1,153 @@
langcode: en
status: true
dependencies:
module:
- media
- user
id: test_media_bulk_form
label: ''
module: views
description: ''
tag: ''
base_table: media_field_data
base_field: mid
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
style:
type: table
row:
type: fields
fields:
media_bulk_form:
id: media_bulk_form
table: media
field: media_bulk_form
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
action_title: 'With selection'
include_exclude: exclude
selected_actions: { }
entity_type: media
plugin_id: bulk_form
name:
id: name
table: media_field_data
field: name
entity_type: media
entity_field: media
hide_empty: false
empty_zero: false
settings:
link_to_entity: false
plugin_id: field
relationship: none
group_type: group
admin_label: ''
label: 'Media name'
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
status:
id: status
table: media_field_data
field: status
relationship: none
group_type: group
admin_label: ''
label: Status
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: boolean
settings:
format: custom
format_custom_true: Published
format_custom_false: Unpublished
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: media
entity_field: status
plugin_id: field
sorts:
mid:
id: mid
table: media_field_data
field: mid
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
entity_type: media
entity_field: mid
plugin_id: standard
title: 'Entity bulk form test view'
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
path: test-media-bulk-form
@@ -0,0 +1,9 @@
name: 'Media test views'
type: module
description: 'Provides default views for views media tests.'
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:media
- drupal:views
@@ -0,0 +1,216 @@
<?php
namespace Drupal\Tests\media\Functional\FieldFormatter;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\media\Entity\Media;
use Drupal\media_test_oembed\Controller\ResourceController;
use Drupal\media_test_oembed\UrlResolver;
use Drupal\Tests\media\Functional\MediaFunctionalTestBase;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
/**
* @covers \Drupal\media\Plugin\Field\FieldFormatter\OEmbedFormatter
*
* @group media
*/
class OEmbedFormatterTest extends MediaFunctionalTestBase {
use OEmbedTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'field_ui',
'link',
'media_test_oembed',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->lockHttpClientToFixtures();
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
}
/**
* Data provider for testRender().
*
* @see ::testRender()
*
* @return array
*/
public function providerRender() {
return [
'Vimeo video' => [
'https://vimeo.com/7073899',
'video_vimeo.json',
[],
[
'iframe' => [
'src' => '/media/oembed?url=https%3A//vimeo.com/7073899',
'width' => '480',
'height' => '360',
'title' => 'Drupal Rap Video - Schipulcon09',
],
],
],
'Vimeo video, resized' => [
'https://vimeo.com/7073899',
'video_vimeo.json?maxwidth=100&maxheight=100',
['max_width' => '100', 'max_height' => '100'],
[
'iframe' => [
'src' => '/media/oembed?url=https%3A//vimeo.com/7073899',
'width' => '100',
'height' => '100',
'title' => 'Drupal Rap Video - Schipulcon09',
],
],
],
'Vimeo video, no title' => [
'https://vimeo.com/7073899',
'video_vimeo-no-title.json',
[],
[
'iframe' => [
'src' => '/media/oembed?url=https%3A//vimeo.com/7073899',
'width' => '480',
'height' => '360',
'title' => NULL,
],
],
],
'tweet' => [
'https://twitter.com/drupaldevdays/status/935643039741202432',
'rich_twitter.json',
[],
[
'iframe' => [
'src' => '/media/oembed?url=https%3A//twitter.com/drupaldevdays/status/935643039741202432',
'width' => '550',
'height' => '360',
],
],
],
'Flickr photo' => [
'https://www.flickr.com/photos/amazeelabs/26497866357',
'photo_flickr.json',
[],
[
'img' => [
'src' => '/core/misc/druplicon.png',
'width' => '88',
'height' => '100',
],
],
],
'Flickr photo (no dimensions)' => [
'https://www.flickr.com/photos/amazeelabs/26497866357',
'photo_flickr_no_dimensions.json',
[],
[
'img' => [
'src' => '/core/misc/druplicon.png',
],
],
],
];
}
/**
* Tests that oEmbed media types' display can be configured correctly.
*/
public function testDisplayConfiguration() {
$account = $this->drupalCreateUser(['administer media display']);
$this->drupalLogin($account);
$media_type = $this->createMediaType('oembed:video');
$this->drupalGet('/admin/structure/media/manage/' . $media_type->id() . '/display');
$assert = $this->assertSession();
$assert->statusCodeEquals(200);
// Test that the formatter doesn't try to check applicability for fields
// which do not have a specific target bundle.
// @see https://www.drupal.org/project/drupal/issues/2976795.
$assert->pageTextNotContains('Can only flip STRING and INTEGER values!');
}
/**
* Tests the oEmbed field formatter.
*
* @param string $url
* The canonical URL of the media asset to test.
* @param string $resource_url
* The oEmebd resource URL of the media asset to test.
* @param mixed $formatter_settings
* Settings for the oEmbed field formatter.
* @param array $selectors
* An array of arrays. Each key is a CSS selector targeting an element in
* the rendered output, and each value is an array of attributes, keyed by
* name, that the element is expected to have.
*
* @dataProvider providerRender
*/
public function testRender($url, $resource_url, array $formatter_settings, array $selectors) {
$account = $this->drupalCreateUser(['view media']);
$this->drupalLogin($account);
$media_type = $this->createMediaType('oembed:video');
$source = $media_type->getSource();
$source_field = $source->getSourceFieldDefinition($media_type);
EntityViewDisplay::create([
'targetEntityType' => 'media',
'bundle' => $media_type->id(),
'mode' => 'full',
'status' => TRUE,
])->removeComponent('thumbnail')
->setComponent($source_field->getName(), [
'type' => 'oembed',
'settings' => $formatter_settings,
])
->save();
$this->hijackProviderEndpoints();
ResourceController::setResourceUrl($url, $this->getFixturesDirectory() . '/' . $resource_url);
UrlResolver::setEndpointUrl($url, $resource_url);
$entity = Media::create([
'bundle' => $media_type->id(),
$source_field->getName() => $url,
]);
$entity->save();
$this->drupalGet($entity->toUrl());
$assert = $this->assertSession();
$assert->statusCodeEquals(200);
foreach ($selectors as $selector => $attributes) {
$element = $assert->elementExists('css', $selector);
foreach ($attributes as $attribute => $value) {
if (isset($value)) {
$this->assertStringContainsString($value, $element->getAttribute($attribute));
}
else {
$this->assertFalse($element->hasAttribute($attribute));
}
}
}
}
}
@@ -0,0 +1,233 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Core\Cache\Cache;
use Drupal\file\Entity\File;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\media\Functional\Rest\MediaResourceTestBase;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\user\Entity\User;
/**
* @group hal
*/
class MediaHalJsonAnonTest extends MediaResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
$file = File::load(1);
$thumbnail = File::load(2);
$author = User::load($this->entity->getOwnerId());
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/media/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/media/camelids',
],
$this->baseUrl . '/rest/relation/media/camelids/field_media_file' => [
[
'href' => $file->createFileUrl(FALSE),
'lang' => 'en',
],
],
$this->baseUrl . '/rest/relation/media/camelids/revision_user' => [
[
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
],
$this->baseUrl . '/rest/relation/media/camelids/thumbnail' => [
[
'href' => $thumbnail->createFileUrl(FALSE),
'lang' => 'en',
],
],
$this->baseUrl . '/rest/relation/media/camelids/uid' => [
[
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
'lang' => 'en',
],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/media/camelids/field_media_file' => [
[
'_links' => [
'self' => [
'href' => $file->createFileUrl(FALSE),
],
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
],
'lang' => 'en',
'uuid' => [
[
'value' => $file->uuid(),
],
],
],
],
$this->baseUrl . '/rest/relation/media/camelids/revision_user' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
[
'value' => $author->uuid(),
],
],
],
],
$this->baseUrl . '/rest/relation/media/camelids/thumbnail' => [
[
'_links' => [
'self' => [
'href' => $thumbnail->createFileUrl(FALSE),
],
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
],
'lang' => 'en',
'uuid' => [
[
'value' => $thumbnail->uuid(),
],
],
],
],
$this->baseUrl . '/rest/relation/media/camelids/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
[
'value' => $author->uuid(),
],
],
'lang' => 'en',
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedFileEntity() {
$normalization = parent::getExpectedNormalizedFileEntity();
$owner = static::$auth ? $this->account : User::load(0);
// Cannot use applyHalFieldNormalization() as it uses the $entity property
// from the test class, which in the case of file upload tests, is the
// parent entity test entity for the file that's created.
// The HAL normalization adds entity reference fields to '_links' and
// '_embedded'.
unset($normalization['uid']);
return $normalization + [
'_links' => [
'self' => [
// @todo This can use a proper link once
// https://www.drupal.org/project/drupal/issues/2907402 is complete.
// This link matches what is generated from from File::url(), a
// resource URL is currently not available.
'href' => file_create_url($normalization['uri'][0]['value']),
],
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
$this->baseUrl . '/rest/relation/file/file/uid' => [
['href' => $this->baseUrl . '/user/' . $owner->id() . '?_format=hal_json'],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/file/file/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $owner->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
[
'value' => $owner->uuid(),
],
],
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/media/camelids',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:hal.settings']);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class MediaHalJsonBasicAuthTest extends MediaHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class MediaHalJsonCookieTest extends MediaHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Tests\media\Functional\Rest\MediaTypeResourceTestBase;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class MediaTypeHalJsonAnonTest extends MediaTypeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Tests\media\Functional\Rest\MediaTypeResourceTestBase;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class MediaTypeHalJsonBasicAuthTest extends MediaTypeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\media\Functional\Hal;
use Drupal\Tests\media\Functional\Rest\MediaTypeResourceTestBase;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class MediaTypeHalJsonCookieTest extends MediaTypeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,408 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\media\Entity\Media;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Basic access tests for Media.
*
* @group media
*/
class MediaAccessTest extends MediaFunctionalTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// This is needed to provide the user cache context for a below assertion.
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test some access control functionality.
*/
public function testMediaAccess() {
$assert_session = $this->assertSession();
$media_type = $this->createMediaType('test');
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
// Create media.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$user_media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
'uid' => $this->nonAdminUser->id(),
]);
$user_media->save();
// We are logged in as admin, so test 'administer media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$this->drupalLogin($this->nonAdminUser);
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
user_role_revoke_permissions($role->id(), ['view media']);
// Test 'create BUNDLE media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = ['create ' . $media_type->id() . ' media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Verify the author can not view the unpublished media item without
// 'view own unpublished media' permission.
$this->grantPermissions($role, ['view media']);
$this->drupalGet('media/' . $user_media->id());
$this->assertNoCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$user_media->setUnpublished()->save();
$this->drupalGet('media/' . $user_media->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$access_result = $user_media->access('view', NULL, TRUE);
$this->assertSame("The user must be the owner and the 'view own unpublished media' permission is required when the media item is unpublished.", $access_result->getReason());
$this->grantPermissions($role, ['view own unpublished media']);
$this->drupalGet('media/' . $user_media->id());
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
// Test 'create media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = ['create media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'edit own BUNDLE media' and 'delete own BUNDLE media' permissions.
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = [
'edit own ' . $user_media->bundle() . ' media',
'delete own ' . $user_media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'edit any BUNDLE media' and 'delete any BUNDLE media' permissions.
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $media->id() . '/delete');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = [
'edit any ' . $media->bundle() . ' media',
'delete any ' . $media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $media->id() . '/delete');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
// Test the 'access media overview' permission.
$this->grantPermissions($role, ['access content overview']);
$this->drupalGet('admin/content');
$assert_session->linkByHrefNotExists('/admin/content/media');
$this->assertCacheContext('user');
// Create a new role, which implicitly checks if the permission exists.
$mediaOverviewRole = $this->createRole(['access content overview', 'access media overview']);
$this->nonAdminUser->addRole($mediaOverviewRole);
$this->nonAdminUser->save();
$this->drupalGet('admin/content');
$assert_session->linkByHrefExists('/admin/content/media');
$this->clickLink('Media');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
$assert_session->elementExists('css', '.view-media');
$assert_session->pageTextContains($this->loggedInUser->getDisplayName());
$assert_session->pageTextContains($this->nonAdminUser->getDisplayName());
$assert_session->linkByHrefExists('/media/' . $media->id());
$assert_session->linkByHrefExists('/media/' . $user_media->id());
}
/**
* Test view access control on the canonical page.
*/
public function testCanonicalMediaAccess() {
$media_type = $this->createMediaType('test');
$assert_session = $this->assertSession();
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
// Create media.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$user_media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
'uid' => $this->nonAdminUser->id(),
]);
$user_media->save();
$this->drupalLogin($this->nonAdminUser);
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
user_role_revoke_permissions($role->id(), ['view media']);
$this->drupalGet('media/' . $media->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$access_result = $media->access('view', NULL, TRUE);
$this->assertSame("The 'view media' permission is required when the media item is published.", $access_result->getReason());
$this->grantPermissions($role, ['view media']);
$this->drupalGet('media/' . $media->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
}
/**
* Tests unpublished media access.
*/
public function testUnpublishedMediaUserAccess() {
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
$assert_session = $this->assertSession();
$media_type = $this->createMediaType('test');
$permissions = [
'view media',
'view own unpublished media',
];
$user_one = $this->drupalCreateUser($permissions);
$user_two = $this->drupalCreateUser($permissions);
// Create media as user one.
$user_media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
'uid' => $user_one->id(),
]);
$user_media->setUnpublished()->save();
// Make sure user two can't access unpublished media.
$this->drupalLogin($user_two);
$this->drupalGet('media/' . $user_media->id());
$assert_session->statusCodeEquals(403);
$this->assertCacheContext('user');
$this->drupalLogout();
// Make sure user one can access own unpublished media.
$this->drupalLogin($user_one);
$this->drupalGet('media/' . $user_media->id());
$assert_session->statusCodeEquals(200);
$this->assertCacheContext('user');
}
/**
* Tests media access of anonymous user.
*/
public function testMediaAnonymousUserAccess() {
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
$assert_session = $this->assertSession();
$media_type = $this->createMediaType('test');
// Create media as anonymous user.
$user_media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
'uid' => 0,
]);
$user_media->save();
$role = Role::load(RoleInterface::ANONYMOUS_ID);
$this->grantPermissions($role, ['view media', 'view own unpublished media']);
$this->drupalLogout();
// Make sure anonymous users can access published media.
$user_media->setPublished()->save();
$this->drupalGet('media/' . $user_media->id());
$assert_session->statusCodeEquals(200);
// Make sure anonymous users can not access unpublished media
// even though role has 'view own unpublished media' permission.
$user_media->setUnpublished()->save();
$this->drupalGet('media/' . $user_media->id());
$assert_session->statusCodeEquals(403);
$this->assertCacheContext('user');
}
/**
* Tests access for embedded medias.
*/
public function testReferencedRendering() {
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
// Create a media type and a entity reference to itself.
$media_type = $this->createMediaType('test');
FieldStorageConfig::create([
'field_name' => 'field_reference',
'entity_type' => 'media',
'type' => 'entity_reference',
'settings' => [
'target_type' => 'media',
],
])->save();
FieldConfig::create([
'field_name' => 'field_reference',
'entity_type' => 'media',
'bundle' => $media_type->id(),
])->save();
$author = $this->drupalCreateUser([
'view media',
'view own unpublished media',
]);
$other_user = $this->drupalCreateUser([
'view media',
'view own unpublished media',
]);
$view_user = $this->drupalCreateUser(['view media']);
$child_title = 'Child media';
$media_child = Media::create([
'name' => $child_title,
'bundle' => $media_type->id(),
'uid' => $author->id(),
]);
$media_child->setUnpublished()->save();
$media_parent = Media::create([
'name' => 'Parent media',
'bundle' => $media_type->id(),
'field_reference' => $media_child->id(),
]);
$media_parent->save();
\Drupal::service('entity_display.repository')->getViewDisplay('media', $media_type->id(), 'full')
->set('content', [])
->setComponent('title', ['type' => 'string'])
->setComponent('field_reference', [
'type' => 'entity_reference_label',
])
->save();
$assert_session = $this->assertSession();
// The author of the child media items should have access to both the parent
// and child.
$this->drupalLogin($author);
$this->drupalGet($media_parent->toUrl());
$this->assertCacheContext('user');
$assert_session->pageTextContains($child_title);
// Other users with the 'view own unpublished media' permission should not
// be able to see the unpublished child media item. The 'user' cache context
// should be added in this case.
$this->drupalLogin($other_user);
$this->drupalGet($media_parent->toUrl());
$this->assertCacheContext('user');
$assert_session->pageTextNotContains($child_title);
// User with just the 'view media' permission should not be able to see the
// child media item. The 'user' cache context should not be added in this
// case.
$this->drupalLogin($view_user);
$this->drupalGet($media_parent->toUrl());
$this->assertNoCacheContext('user');
$assert_session->pageTextNotContains($child_title);
}
}
@@ -0,0 +1,118 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
use Drupal\views\Views;
/**
* Tests a media bulk form.
*
* @group media
*/
class MediaBulkFormTest extends MediaFunctionalTestBase {
/**
* Modules to be enabled.
*
* @var array
*/
public static $modules = ['media_test_views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The test media type.
*
* @var \Drupal\media\MediaTypeInterface
*/
protected $testMediaType;
/**
* The test media items.
*
* @var \Drupal\media\MediaInterface[]
*/
protected $mediaItems;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->testMediaType = $this->createMediaType('test');
// Create some test media items.
$this->mediaItems = [];
for ($i = 1; $i <= 5; $i++) {
$media = Media::create([
'bundle' => $this->testMediaType->id(),
]);
$media->save();
$this->mediaItems[] = $media;
}
}
/**
* Tests the media bulk form.
*/
public function testBulkForm() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
// Check that all created items are present in the test view.
$view = Views::getView('test_media_bulk_form');
$view->execute();
$this->assertSame($view->total_rows, 5);
// Check the operations are accessible to the logged in user.
$this->drupalGet('test-media-bulk-form');
// Current available actions: Delete, Save, Publish, Unpublish.
$available_actions = [
'media_delete_action',
'media_publish_action',
'media_save_action',
'media_unpublish_action',
];
foreach ($available_actions as $action_name) {
$assert_session->optionExists('action', $action_name);
}
// Test unpublishing in bulk.
$page->checkField('media_bulk_form[0]');
$page->checkField('media_bulk_form[1]');
$page->checkField('media_bulk_form[2]');
$page->selectFieldOption('action', 'media_unpublish_action');
$page->pressButton('Apply to selected items');
$assert_session->pageTextContains('Unpublish media was applied to 3 items');
$this->assertFalse($this->storage->loadUnchanged(1)->isPublished(), 'The unpublish action failed in some of the media items.');
$this->assertFalse($this->storage->loadUnchanged(2)->isPublished(), 'The unpublish action failed in some of the media items.');
$this->assertFalse($this->storage->loadUnchanged(3)->isPublished(), 'The unpublish action failed in some of the media items.');
// Test publishing in bulk.
$page->checkField('media_bulk_form[0]');
$page->checkField('media_bulk_form[1]');
$page->selectFieldOption('action', 'media_publish_action');
$page->pressButton('Apply to selected items');
$assert_session->pageTextContains('Publish media was applied to 2 items');
$this->assertTrue($this->storage->loadUnchanged(1)->isPublished(), 'The publish action failed in some of the media items.');
$this->assertTrue($this->storage->loadUnchanged(2)->isPublished(), 'The publish action failed in some of the media items.');
// Test deletion in bulk.
$page->checkField('media_bulk_form[0]');
$page->checkField('media_bulk_form[1]');
$page->selectFieldOption('action', 'media_delete_action');
$page->pressButton('Apply to selected items');
$assert_session->pageTextContains('Are you sure you want to delete these media items?');
$page->pressButton('Delete');
$assert_session->pageTextContains('Deleted 2 items.');
$this->assertNull($this->storage->loadUnchanged(1), 'Could not delete some of the media items.');
$this->assertNull($this->storage->loadUnchanged(2), 'Could not delete some of the media items.');
}
}
@@ -0,0 +1,80 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Core\Entity\EntityInterface;
use Drupal\media\Entity\Media;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
/**
* Tests the media items cache tags.
*
* @group media
*/
class MediaCacheTagsTest extends EntityWithUriCacheTagsTestBase {
use MediaTypeCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'media',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a media type.
$mediaType = $this->createMediaType('test');
// Create a media item.
$media = Media::create([
'bundle' => $mediaType->id(),
'name' => 'Unnamed',
]);
$media->save();
return $media;
}
/**
* {@inheritdoc}
*/
protected function getAdditionalCacheContextsForEntity(EntityInterface $media) {
return ['timezone'];
}
/**
* {@inheritdoc}
*/
protected function getAdditionalCacheTagsForEntity(EntityInterface $media) {
// Each media item must have an author and a thumbnail.
return [
'user:' . $media->getOwnerId(),
'config:image.style.thumbnail',
'file:' . $media->get('thumbnail')->entity->id(),
];
}
}
@@ -0,0 +1,57 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
/**
* Tests views contextual links on media items.
*
* @group media
*/
class MediaContextualLinksTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'contextual',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests contextual links.
*/
public function testMediaContextualLinks() {
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
// Create a media type.
$mediaType = $this->createMediaType('test');
// Create a media item.
$media = Media::create([
'bundle' => $mediaType->id(),
'name' => 'Unnamed',
]);
$media->save();
$user = $this->drupalCreateUser([
'administer media',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('media/' . $media->id());
$this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'media:media=' . $media->id() . ':');
}
}
@@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
/**
* Base class for Media functional tests.
*/
abstract class MediaFunctionalTestBase extends BrowserTestBase {
use MediaFunctionalTestTrait;
use MediaTypeCreationTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'system',
'node',
'field_ui',
'views_ui',
'media',
'media_test_source',
];
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\Tests\media\Functional;
@trigger_error(__NAMESPACE__ . '\MediaFunctionalTestCreateMediaTypeTrait is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\media\Traits\MediaTypeCreationTrait instead. See https://www.drupal.org/node/2981614.', E_USER_DEPRECATED);
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
/**
* Trait with helpers for Media functional tests.
*
* @deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use
* \Drupal\Tests\media\Traits\MediaTypeCreationTrait instead.
*
* @see https://www.drupal.org/node/2981614
*/
trait MediaFunctionalTestCreateMediaTypeTrait {
use MediaTypeCreationTrait {
createMediaType as traitCreateMediaType;
}
/**
* Creates a media type.
*
* @param array $values
* The media type values.
* @param string $source
* (optional) The media source plugin that is responsible for additional
* logic related to this media type. Defaults to 'test'.
*
* @return \Drupal\media\MediaTypeInterface
* A newly created media type.
*/
protected function createMediaType(array $values = [], $source = 'test') {
return $this->traitCreateMediaType($source, $values);
}
}
@@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\media\Functional;
/**
* Trait with helpers for Media functional tests.
*/
trait MediaFunctionalTestTrait {
/**
* Permissions for the admin user that will be logged-in for test.
*
* @var array
*/
protected static $adminUserPermissions = [
// Media module permissions.
'access media overview',
'administer media',
'administer media fields',
'administer media form display',
'administer media display',
'administer media types',
'view media',
// Other permissions.
'administer views',
'access content overview',
'view all revisions',
'administer content types',
'administer node fields',
'administer node form display',
'administer node display',
'bypass node access',
];
/**
* An admin test user account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $adminUser;
/**
* A non-admin test user account.
*
* @var \Drupal\user\UserInterface
*/
protected $nonAdminUser;
/**
* The storage service.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Have two users ready to be used in tests.
$this->adminUser = $this->drupalCreateUser(static::$adminUserPermissions);
$this->nonAdminUser = $this->drupalCreateUser([]);
// Start off logged in as admin.
$this->drupalLogin($this->adminUser);
$this->storage = $this->container->get('entity_type.manager')->getStorage('media');
}
}
@@ -0,0 +1,49 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests media Install / Uninstall logic.
*
* @group media
*/
class MediaInstallTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['administer modules']));
}
/**
* Tests reinstalling after being uninstalled.
*/
public function testReinstallAfterUninstall() {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// Uninstall the media module.
$this->container->get('module_installer')->uninstall(['media'], FALSE);
$this->drupalGet('/admin/modules');
$page->checkField('modules[media][enable]');
$page->pressButton('Install');
$assert_session->pageTextNotContains('could not be moved/copied because a file by that name already exists in the destination directory');
$assert_session->pageTextContains('Module Media has been enabled');
}
}
@@ -0,0 +1,174 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the Media overview page.
*
* @group media
*/
class MediaOverviewPageTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->nonAdminUser);
}
/**
* Test that the Media overview page (/admin/content/media).
*/
public function testMediaOverviewPage() {
$assert_session = $this->assertSession();
// Check the view exists, is access-restricted, and some defaults are there.
$this->drupalGet('/admin/content/media');
$assert_session->statusCodeEquals(403);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
$this->grantPermissions($role, ['access media overview']);
$this->getSession()->reload();
$assert_session->statusCodeEquals(200);
$assert_session->titleEquals('Media | Drupal');
$assert_session->fieldExists('Media name');
$assert_session->selectExists('type');
$assert_session->selectExists('status');
$assert_session->selectExists('langcode');
$assert_session->buttonExists('Filter');
$header = $assert_session->elementExists('css', 'th#view-thumbnail-target-id-table-column');
$this->assertSame('Thumbnail', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-name-table-column');
$this->assertSame('Media name', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-bundle-table-column');
$this->assertSame('Type', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-uid-table-column');
$this->assertSame('Author', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-status-table-column');
$this->assertSame('Status', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-changed-table-column');
$this->assertSame('Updated Sort ascending', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-operations-table-column');
$this->assertSame('Operations', $header->getText());
$assert_session->pageTextContains('No media available.');
// Create some content for the view.
$media_type1 = $this->createMediaType('test');
$media_type2 = $this->createMediaType('test');
$media1 = Media::create([
'bundle' => $media_type1->id(),
'name' => 'Media 1',
'uid' => $this->adminUser->id(),
]);
$media1->save();
$media2 = Media::create([
'bundle' => $media_type2->id(),
'name' => 'Media 2',
'uid' => $this->adminUser->id(),
'status' => FALSE,
'changed' => time() - 50,
]);
$media2->save();
$media3 = Media::create([
'bundle' => $media_type1->id(),
'name' => 'Media 3',
'uid' => $this->nonAdminUser->id(),
'changed' => time() - 100,
]);
$media3->save();
// Verify the view is now correctly populated. The non-admin user can only
// view published media.
$this->grantPermissions($role, [
'view media',
'update any media',
'delete any media',
]);
$this->getSession()->reload();
$row1 = $assert_session->elementExists('css', 'table tbody tr:nth-child(1)');
$row2 = $assert_session->elementExists('css', 'table tbody tr:nth-child(2)');
// Media thumbnails.
$assert_session->elementExists('css', 'td.views-field-thumbnail__target-id img', $row1);
$assert_session->elementExists('css', 'td.views-field-thumbnail__target-id img', $row2);
// Media names.
$name1 = $assert_session->elementExists('css', 'td.views-field-name a', $row1);
$this->assertSame($media1->label(), $name1->getText());
$name2 = $assert_session->elementExists('css', 'td.views-field-name a', $row2);
$this->assertSame($media3->label(), $name2->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id());
$assert_session->linkByHrefExists('/media/' . $media3->id());
// Media types.
$type_element1 = $assert_session->elementExists('css', 'td.views-field-bundle', $row1);
$this->assertSame($media_type1->label(), $type_element1->getText());
$type_element2 = $assert_session->elementExists('css', 'td.views-field-bundle', $row2);
$this->assertSame($media_type1->label(), $type_element2->getText());
// Media authors.
$author_element1 = $assert_session->elementExists('css', 'td.views-field-uid', $row1);
$this->assertSame($this->adminUser->getDisplayName(), $author_element1->getText());
$author_element3 = $assert_session->elementExists('css', 'td.views-field-uid', $row2);
$this->assertSame($this->nonAdminUser->getDisplayName(), $author_element3->getText());
// Media publishing status.
$status_element1 = $assert_session->elementExists('css', 'td.views-field-status', $row1);
$this->assertSame('Published', $status_element1->getText());
$status_element3 = $assert_session->elementExists('css', 'td.views-field-status', $row2);
$this->assertSame('Published', $status_element3->getText());
// Timestamp.
$expected = \Drupal::service('date.formatter')->format($media1->getChangedTime(), 'short');
$changed_element1 = $assert_session->elementExists('css', 'td.views-field-changed', $row1);
$this->assertSame($expected, $changed_element1->getText());
// Operations.
$edit_link1 = $assert_session->elementExists('css', 'td.views-field-operations li.edit a', $row1);
$this->assertSame('Edit', $edit_link1->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id() . '/edit');
$delete_link1 = $assert_session->elementExists('css', 'td.views-field-operations li.delete a', $row1);
$this->assertSame('Delete', $delete_link1->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id() . '/delete');
// Make the user the owner of the unpublished media item and assert the
// media item is only visible with the 'view own unpublished media'
// permission.
$media2->setOwner($this->nonAdminUser)->save();
$this->getSession()->reload();
$assert_session->pageTextNotContains($media2->label());
$role->grantPermission('view own unpublished media')->save();
$this->getSession()->reload();
$row = $assert_session->elementExists('css', 'table tbody tr:nth-child(2)');
$name = $assert_session->elementExists('css', 'td.views-field-name a', $row);
$this->assertSame($media2->label(), $name->getText());
$status_element = $assert_session->elementExists('css', 'td.views-field-status', $row);
$this->assertSame('Unpublished', $status_element->getText());
// Assert the admin user can always view all media.
$this->drupalLogin($this->adminUser);
$this->drupalGet('/admin/content/media');
$row1 = $assert_session->elementExists('css', 'table tbody tr:nth-child(1)');
$row2 = $assert_session->elementExists('css', 'table tbody tr:nth-child(2)');
$row3 = $assert_session->elementExists('css', 'table tbody tr:nth-child(3)');
$name1 = $assert_session->elementExists('css', 'td.views-field-name a', $row1);
$this->assertSame($media1->label(), $name1->getText());
$name2 = $assert_session->elementExists('css', 'td.views-field-name a', $row2);
$this->assertSame($media2->label(), $name2->getText());
$name3 = $assert_session->elementExists('css', 'td.views-field-name a', $row3);
$this->assertSame($media3->label(), $name3->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id());
$assert_session->linkByHrefExists('/media/' . $media2->id());
$assert_session->linkByHrefExists('/media/' . $media3->id());
}
}
@@ -0,0 +1,209 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\media\MediaInterface;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the revisionability of media entities.
*
* @group media
*/
class MediaRevisionTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Checks media revision operations.
*/
public function testRevisions() {
$assert = $this->assertSession();
/** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $media_storage */
$media_storage = $this->container->get('entity_type.manager')->getStorage('media');
// Create a media type and media item.
$media_type = $this->createMediaType('test');
$media = $media_storage->create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
// You can't access the revision page when there is only 1 revision.
$this->drupalGet('media/' . $media->id() . '/revisions/' . $media->getRevisionId() . '/view');
$assert->statusCodeEquals(403);
// Create some revisions.
$media_revisions = [];
$media_revisions[] = clone $media;
$revision_count = 3;
for ($i = 0; $i < $revision_count; $i++) {
$media->revision_log = $this->randomMachineName(32);
$media = $this->createMediaRevision($media);
$media_revisions[] = clone $media;
}
// Get the last revision for simple checks.
/** @var \Drupal\media\MediaInterface $media */
$media = end($media_revisions);
// Test permissions.
$this->drupalLogin($this->nonAdminUser);
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'view all media revisions' permission ('view media' permission is
// needed as well).
user_role_revoke_permissions($role->id(), ['view media', 'view all media revisions']);
$this->drupalGet('media/' . $media->id() . '/revisions/' . $media->getRevisionId() . '/view');
$assert->statusCodeEquals(403);
$this->grantPermissions($role, ['view media', 'view all media revisions']);
$this->drupalGet('media/' . $media->id() . '/revisions/' . $media->getRevisionId() . '/view');
$assert->statusCodeEquals(200);
// Confirm the revision page shows the correct title.
$assert->pageTextContains($media->getName());
// Confirm that the last revision is the default revision.
$this->assertTrue($media->isDefaultRevision(), 'Last revision is the default.');
}
/**
* Tests creating revisions of a File media item.
*/
public function testFileMediaRevision() {
$assert = $this->assertSession();
$uri = 'temporary://foo.txt';
file_put_contents($uri, $this->randomString(128));
$this->createMediaType('file', ['id' => 'document', 'new_revision' => TRUE]);
// Create a media item.
$this->drupalGet('/media/add/document');
$page = $this->getSession()->getPage();
$page->fillField('Name', 'Foobar');
$page->attachFileToField('File', $this->container->get('file_system')->realpath($uri));
$page->pressButton('Save');
$assert->addressEquals('admin/content/media');
// The media item was just created, so it should only have one revision.
$media = $this->container
->get('entity_type.manager')
->getStorage('media')
->load(1);
$this->assertRevisionCount($media, 1);
// If we edit the item, we should get a new revision.
$this->drupalGet('/media/1/edit');
$assert->checkboxChecked('Create new revision');
$page = $this->getSession()->getPage();
$page->fillField('Name', 'Foobaz');
$page->pressButton('Save');
$this->assertRevisionCount($media, 2);
// Confirm the correct revision title appears on "view revisions" page.
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged(1);
$this->drupalGet("media/" . $media->id() . "/revisions/" . $media->getRevisionId() . "/view");
$assert->pageTextContains('Foobaz');
}
/**
* Tests creating revisions of a Image media item.
*/
public function testImageMediaRevision() {
$assert = $this->assertSession();
$this->createMediaType('image', ['id' => 'image', 'new_revision' => TRUE]);
/** @var \Drupal\field\FieldConfigInterface $field */
// Disable the alt text field, because this is not a JavaScript test and
// the alt text field will therefore not appear without a full page refresh.
$field = FieldConfig::load('media.image.field_media_image');
$settings = $field->getSettings();
$settings['alt_field'] = FALSE;
$settings['alt_field_required'] = FALSE;
$field->set('settings', $settings);
$field->save();
// Create a media item.
$this->drupalGet('/media/add/image');
$page = $this->getSession()->getPage();
$page->fillField('Name', 'Foobar');
$page->attachFileToField('Image', $this->root . '/core/modules/media/tests/fixtures/example_1.jpeg');
$page->pressButton('Save');
$assert->addressEquals('admin/content/media');
// The media item was just created, so it should only have one revision.
$media = $this->container
->get('entity_type.manager')
->getStorage('media')
->load(1);
$this->assertRevisionCount($media, 1);
// If we edit the item, we should get a new revision.
$this->drupalGet('/media/1/edit');
$assert->checkboxChecked('Create new revision');
$page = $this->getSession()->getPage();
$page->fillField('Name', 'Foobaz');
$page->pressButton('Save');
$this->assertRevisionCount($media, 2);
// Confirm the correct revision title appears on "view revisions" page.
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged(1);
$this->drupalGet("media/" . $media->id() . "/revisions/" . $media->getRevisionId() . "/view");
$assert->pageTextContains('Foobaz');
}
/**
* Creates a new revision for a given media item.
*
* @param \Drupal\media\MediaInterface $media
* A media object.
*
* @return \Drupal\media\MediaInterface
* A media object with up to date revision information.
*/
protected function createMediaRevision(MediaInterface $media) {
$media->setName($this->randomMachineName());
$media->setNewRevision();
$media->save();
return $media;
}
/**
* Asserts that an entity has a certain number of revisions.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity in question.
* @param int $expected_revisions
* The expected number of revisions.
*/
protected function assertRevisionCount(EntityInterface $entity, $expected_revisions) {
$entity_type = $entity->getEntityType();
$count = $this->container
->get('entity_type.manager')
->getStorage($entity_type->id())
->getQuery()
->count()
->allRevisions()
->condition($entity_type->getKey('id'), $entity->id())
->execute();
$this->assertSame($expected_revisions, (int) $count);
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\media\Functional;
/**
* Testing the media settings.
*
* @group media
*/
class MediaSettingsTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->createUser(['administer site configuration']));
}
/**
* Test that media warning appears if oEmbed media types exists.
*/
public function testStatusPage() {
$assert_session = $this->assertSession();
$this->drupalGet('admin/reports/status');
$assert_session->pageTextNotContains('It is potentially insecure to display oEmbed content in a frame');
$this->createMediaType('oembed:video');
$this->drupalGet('admin/reports/status');
$assert_session->pageTextContains('It is potentially insecure to display oEmbed content in a frame');
}
}
@@ -0,0 +1,58 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\field\Entity\FieldConfig;
/**
* Tests the file media source.
*
* @group media
*/
class MediaSourceFileTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Test that it's possible to change the allowed file extensions.
*/
public function testSourceFieldSettingsEditing() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$media_type = $this->createMediaType('file');
$media_type_id = $media_type->id();
$this->assertSame('txt doc docx pdf', FieldConfig::load("media.$media_type_id.field_media_file")->get('settings')['file_extensions']);
$this->drupalGet("admin/structure/media/manage/$media_type_id/fields/media.$media_type_id.field_media_file");
// File extension field exists.
$assert_session->fieldExists('Allowed file extensions');
// Add another extension.
$page->fillField('settings[file_extensions]', 'txt, doc, docx, pdf, odt');
$page->pressButton('Save settings');
$this->drupalGet("admin/structure/media/manage/$media_type_id/fields/media.$media_type_id.field_media_file");
// Verify that new extension is present.
$assert_session->fieldValueEquals('settings[file_extensions]', 'txt, doc, docx, pdf, odt');
$this->assertSame('txt doc docx pdf odt', FieldConfig::load("media.$media_type_id.field_media_file")->get('settings')['file_extensions']);
}
/**
* Ensure source field deletion is not possible.
*/
public function testPreventSourceFieldDeletion() {
$media_type = $this->createMediaType('file');
$media_type_id = $media_type->id();
$this->drupalGet("admin/structure/media/manage/$media_type_id/fields/media.$media_type_id.field_media_file/delete");
$this->assertSession()->statusCodeEquals(403);
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
/**
* Tests media template suggestions.
*
* @group media
*/
class MediaTemplateSuggestionsTest extends MediaFunctionalTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests template suggestions from media_theme_suggestions_media().
*/
public function testMediaThemeHookSuggestions() {
$media_type = $this->createMediaType('test', [
'queue_thumbnail_downloads' => FALSE,
]);
// Create media item to be rendered.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$view_mode = 'full';
// Simulate theming of the media item.
$build = \Drupal::entityTypeManager()->getViewBuilder('media')->view($media, $view_mode);
$variables['elements'] = $build;
$suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_media', [$variables]);
$this->assertSame($suggestions, ['media__full', 'media__' . $media_type->id(), 'media__' . $media_type->id() . '__full', 'media__source_' . $media_type->getSource()->getPluginId()], 'Found expected media suggestions.');
}
}
@@ -0,0 +1,103 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
/**
* Tests the Media Translation UI.
*
* @group media
*/
class MediaTranslationUITest extends ContentTranslationUITestBase {
use MediaTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* {inheritdoc}
*/
protected $defaultCacheContexts = [
'languages:language_interface',
'session',
'theme',
'url.path',
'url.query_args',
'user.permissions',
'user.roles:authenticated',
];
/**
* {inheritdoc}
*/
public static $modules = [
'language',
'content_translation',
'media',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->entityTypeId = 'media';
$this->bundle = 'test';
parent::setUp();
}
/**
* {@inheritdoc}
*/
public function setupBundle() {
$this->createMediaType('test', [
'id' => $this->bundle,
'queue_thumbnail_downloads' => FALSE,
]);
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), [
'administer media',
'edit any test media',
]);
}
/**
* {@inheritdoc}
*/
protected function getEditorPermissions() {
return ['administer media', 'create test media'];
}
/**
* {@inheritdoc}
*/
protected function getAdministratorPermissions() {
return array_merge(parent::getAdministratorPermissions(), [
'access administration pages',
'administer media types',
'access media overview',
'administer languages',
]);
}
/**
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
return [
'name' => [['value' => $this->randomMachineName()]],
'field_media_test' => [['value' => $this->randomMachineName()]],
] + parent::getNewEntityValues($langcode);
}
}
@@ -0,0 +1,47 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\MediaType;
/**
* Ensures that media UI works correctly without JavaScript.
*
* @group media
*/
class MediaTypeCreationTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the media type creation form with only the mandatory options.
*/
public function testMediaTypeCreationForm() {
$machine_name = mb_strtolower($this->randomMachineName());
$this->drupalGet('/admin/structure/media/add');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->fieldExists('label')->setValue($this->randomString());
$this->assertSession()->fieldExists('id')->setValue($machine_name);
$this->assertSession()->selectExists('source')->selectOption('test');
$this->assertSession()->buttonExists('Save')->press();
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->fieldValueEquals('Test config value', 'This is default value.');
$this->assertSession()->buttonExists('Save')->press();
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->addressEquals('admin/structure/media');
$this->assertInstanceOf(MediaType::class, MediaType::load($machine_name));
}
}
@@ -0,0 +1,566 @@
<?php
namespace Drupal\Tests\media\Functional;
use Behat\Mink\Element\NodeElement;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Ensures that media UI works correctly.
*
* @group media
*/
class MediaUiFunctionalTest extends MediaFunctionalTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'block',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests the media actions (add/edit/delete).
*/
public function testMediaWithOnlyOneMediaType() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$media_type = $this->createMediaType('test', [
'queue_thumbnail_downloads' => FALSE,
]);
$this->drupalGet('media/add');
$assert_session->statusCodeEquals(200);
$assert_session->addressEquals('media/add/' . $media_type->id());
$assert_session->elementNotExists('css', '#edit-revision');
// Tests media add form.
$media_name = $this->randomMachineName();
$page->fillField('name[0][value]', $media_name);
$revision_log_message = $this->randomString();
$page->fillField('revision_log_message[0][value]', $revision_log_message);
$source_field = $this->randomString();
$page->fillField('field_media_test[0][value]', $source_field);
$page->pressButton('Save');
$media_id = $this->container->get('entity_type.manager')
->getStorage('media')
->getQuery()
->execute();
$media_id = reset($media_id);
/** @var \Drupal\media\MediaInterface $media */
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged($media_id);
$this->assertSame($media->getRevisionLogMessage(), $revision_log_message);
$this->assertSame($media->getName(), $media_name);
// Tests media edit form.
$media_type->setNewRevision(FALSE);
$media_type->save();
$media_name2 = $this->randomMachineName();
$this->drupalGet('media/' . $media_id . '/edit');
$assert_session->checkboxNotChecked('edit-revision');
$media_name = $this->randomMachineName();
$page->fillField('name[0][value]', $media_name2);
$page->pressButton('Save');
/** @var \Drupal\media\MediaInterface $media */
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged($media_id);
$this->assertSame($media->getName(), $media_name2);
// Test that there is no empty vertical tabs element, if the container is
// empty (see #2750697).
// Make the "Publisher ID" and "Created" fields hidden.
$this->drupalGet('/admin/structure/media/manage/' . $media_type->id() . '/form-display');
$page->selectFieldOption('fields[created][parent]', 'hidden');
$page->selectFieldOption('fields[uid][parent]', 'hidden');
$page->pressButton('Save');
// Assure we are testing with a user without permission to manage revisions.
$this->drupalLogin($this->nonAdminUser);
// Check the container is not present.
$this->drupalGet('media/' . $media_id . '/edit');
$assert_session->elementNotExists('css', 'input.vertical-tabs__active-tab');
// Continue testing as admin.
$this->drupalLogin($this->adminUser);
// Enable revisions by default.
$previous_revision_id = $media->getRevisionId();
$media_type->setNewRevision(TRUE);
$media_type->save();
$this->drupalGet('media/' . $media_id . '/edit');
$assert_session->checkboxChecked('edit-revision');
$page->fillField('name[0][value]', $media_name);
$page->fillField('revision_log_message[0][value]', $revision_log_message);
$page->pressButton('Save');
$this->drupalGet('media/' . $media_id);
$assert_session->statusCodeEquals(404);
/** @var \Drupal\media\MediaInterface $media */
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged($media_id);
$this->assertSame($media->getRevisionLogMessage(), $revision_log_message);
$this->assertNotEquals($previous_revision_id, $media->getRevisionId());
// Test the status checkbox.
$this->drupalGet('media/' . $media_id . '/edit');
$page->uncheckField('status[value]');
$page->pressButton('Save');
/** @var \Drupal\media\MediaInterface $media */
$media = $this->container->get('entity_type.manager')
->getStorage('media')
->loadUnchanged($media_id);
$this->assertFalse($media->isPublished());
// Tests media delete form.
$this->drupalGet('media/' . $media_id . '/edit');
$page->clickLink('Delete');
$assert_session->pageTextContains('This action cannot be undone');
$page->pressButton('Delete');
$media_id = \Drupal::entityQuery('media')->execute();
$this->assertEmpty($media_id);
}
/**
* Tests the "media/add" page.
*
* Tests if the "media/add" page gives you a selecting option if there are
* multiple media types available.
*/
public function testMediaWithMultipleMediaTypes() {
$assert_session = $this->assertSession();
// Tests and creates the first media type.
$first_media_type = $this->createMediaType('test', ['description' => $this->randomMachineName()]);
// Test and create a second media type.
$second_media_type = $this->createMediaType('test', ['description' => $this->randomMachineName()]);
// Test if media/add displays two media type options.
$this->drupalGet('media/add');
// Checks for the first media type.
$assert_session->pageTextContains($first_media_type->label());
$assert_session->pageTextContains($first_media_type->getDescription());
// Checks for the second media type.
$assert_session->pageTextContains($second_media_type->label());
$assert_session->pageTextContains($second_media_type->getDescription());
}
/**
* Test that media in ER fields use the Rendered Entity formatter by default.
*/
public function testRenderedEntityReferencedMedia() {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
$this->drupalGet('/admin/structure/types/manage/page/fields/add-field');
$page->selectFieldOption('new_storage_type', 'field_ui:entity_reference:media');
$page->fillField('label', 'Foo field');
$page->fillField('field_name', 'foo_field');
$page->pressButton('Save and continue');
$this->drupalGet('/admin/structure/types/manage/page/display');
$assert_session->fieldValueEquals('fields[field_foo_field][type]', 'entity_reference_entity_view');
}
/**
* Data provider for testMediaReferenceWidget().
*
* @return array[]
* Test data. See testMediaReferenceWidget() for the child array structure.
*/
public function providerTestMediaReferenceWidget() {
return [
// Single-value fields with a single media type and the default widget:
// - The user can create and list the media.
'single_value:single_type:create_list' => [1, [TRUE], TRUE],
// - The user can list but not create the media.
'single_value:single_type:list' => [1, [FALSE], TRUE],
// - The user can create but not list the media.
'single_value:single_type:create' => [1, [TRUE], FALSE],
// - The user can neither create nor list the media.
'single_value:single_type' => [1, [FALSE], FALSE],
// Single-value fields with the tags-style widget:
// - The user can create and list the media.
'single_value:single_type:create_list:tags' => [1, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
// - The user can list but not create the media.
'single_value:single_type:list:tags' => [1, [FALSE], TRUE, 'entity_reference_autocomplete_tags'],
// - The user can create but not list the media.
'single_value:single_type:create:tags' => [1, [TRUE], FALSE, 'entity_reference_autocomplete_tags'],
// - The user can neither create nor list the media.
'single_value:single_type:tags' => [1, [FALSE], FALSE, 'entity_reference_autocomplete_tags'],
// Single-value fields with two media types:
// - The user can create both types.
'single_value:two_type:create2_list' => [1, [TRUE, TRUE], TRUE],
// - The user can create only one type.
'single_value:two_type:create1_list' => [1, [TRUE, FALSE], TRUE],
// - The user cannot create either type.
'single_value:two_type:list' => [1, [FALSE, FALSE], TRUE],
// Multiple-value field with a cardinality of 3, with media the user can
// create and list.
'multi_value:single_type:create_list' => [3, [TRUE], TRUE],
// The same, with the tags field.
'multi-value:single_type:create_list:tags' => [3, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
// Unlimited value field.
'unlimited_value:single_type:create_list' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE],
// Unlimited value field with the tags widget.
'unlimited_value:single_type:create_list:tags' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
];
}
/**
* Tests the default autocomplete widgets for media reference fields.
*
* @param int $cardinality
* The field cardinality.
* @param bool[] $media_type_create_access
* An array of booleans indicating whether to grant the test user create
* access for each media type. A media type is created automatically for
* each; for example, an array [TRUE, FALSE] would create two media types,
* one that allows the user to create media and a second that does not.
* @param bool $list_access
* Whether to grant the test user access to list media.
*
* @see media_field_widget_entity_reference_autocomplete_form_alter()
* @see media_field_widget_multiple_entity_reference_autocomplete_form_alter()
*
* @dataProvider providerTestMediaReferenceWidget
*/
public function testMediaReferenceWidget($cardinality, array $media_type_create_access, $list_access, $widget_id = 'entity_reference_autocomplete') {
$assert_session = $this->assertSession();
// Create two content types.
$non_media_content_type = $this->createContentType();
$content_type = $this->createContentType();
// Create some media types.
$media_types = [];
$permissions = [];
$create_media_types = [];
foreach ($media_type_create_access as $id => $access) {
if ($access) {
$create_media_types[] = "media_type_$id";
$permissions[] = "create media_type_$id media";
}
$this->createMediaType('test', [
'id' => "media_type_$id",
'label' => "media_type_$id",
]);
$media_types["media_type_$id"] = "media_type_$id";
}
// Create a user that can create content of the type, with other
// permissions as given by the data provider.
$permissions[] = "create {$content_type->id()} content";
if ($list_access) {
$permissions[] = "access media overview";
}
$test_user = $this->drupalCreateUser($permissions);
// Create a non-media entity reference.
$non_media_storage = FieldStorageConfig::create([
'field_name' => 'field_not_a_media_field',
'entity_type' => 'node',
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'node',
],
]);
$non_media_storage->save();
$non_media_field = FieldConfig::create([
'label' => 'No media here!',
'field_storage' => $non_media_storage,
'entity_type' => 'node',
'bundle' => $non_media_content_type->id(),
'settings' => [
'handler' => 'default',
'handler_settings' => [
'target_bundles' => [
$non_media_content_type->id() => $non_media_content_type->id(),
],
],
],
]);
$non_media_field->save();
\Drupal::entityTypeManager()
->getStorage('entity_form_display')
->load('node.' . $non_media_content_type->id() . '.default')
->setComponent('field_not_a_media_field', [
'type' => $widget_id,
])
->save();
// Create a media field through the user interface to ensure that the
// help text handling does not break the default value entry on the field
// settings form.
// Using drupalPostForm() to avoid dealing with JavaScript on the previous
// page in the field creation.
$edit = [
'new_storage_type' => 'field_ui:entity_reference:media',
'label' => "Media (cardinality $cardinality)",
'field_name' => 'media_reference',
];
$this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/add-field", $edit, 'Save and continue');
$edit = [];
foreach ($media_types as $type) {
$edit["settings[handler_settings][target_bundles][$type]"] = TRUE;
}
$this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/node.{$content_type->id()}.field_media_reference", $edit, "Save settings");
\Drupal::entityTypeManager()
->getStorage('entity_form_display')
->load('node.' . $content_type->id() . '.default')
->setComponent('field_media_reference', [
'type' => $widget_id,
])
->save();
// Some of the expected texts.
$create_help = 'Create your media on the media add page (opens a new window), then add it by name to the field below.';
$list_text = 'See the media list (opens a new window) to help locate media.';
$use_help = 'Type part of the media name.';
$create_header = "Create new media";
$use_header = "Use existing media";
// First check that none of the help texts are on the non-media content.
$this->drupalGet("/node/add/{$non_media_content_type->id()}");
$this->assertNoHelpTexts([
$create_header,
$create_help,
$use_header,
$use_help,
$list_text,
'Allowed media types:',
]);
// Now, check that the widget displays the expected help text under the
// given conditions for the test user.
$this->drupalLogin($test_user);
$this->drupalGet("/node/add/{$content_type->id()}");
// Specific expected help texts for the media field.
$create_header = "Create new media";
$use_header = "Use existing media";
$type_list = 'Allowed media types: ' . implode(", ", array_keys($media_types));
$fieldset_selector = '#edit-field-media-reference-wrapper fieldset';
$fieldset = $assert_session->elementExists('css', $fieldset_selector);
$this->assertSame("Media (cardinality $cardinality)", $assert_session->elementExists('css', 'legend', $fieldset)->getText());
// Assert text that should be displayed regardless of other access.
$this->assertHelpTexts([$use_header, $use_help, $type_list], $fieldset_selector);
// The entire section for creating new media should only be displayed if
// the user can create at least one media of the type.
if ($create_media_types) {
if (count($create_media_types) === 1) {
$url = Url::fromRoute('entity.media.add_form')->setRouteParameter('media_type', $create_media_types[0]);
}
else {
$url = Url::fromRoute('entity.media.add_page');
}
$this->assertHelpTexts([$create_header, $create_help], $fieldset_selector);
$this->assertHelpLink(
$fieldset,
'media add page',
[
'target' => '_blank',
'href' => $url->toString(),
]
);
}
else {
$this->assertNoHelpTexts([$create_header, $create_help]);
$this->assertNoHelpLink($fieldset, 'media add page');
}
if ($list_access) {
$this->assertHelpTexts([$list_text], $fieldset_selector);
$this->assertHelpLink(
$fieldset,
'media list',
[
'target' => '_blank',
'href' => Url::fromRoute('entity.media.collection')->toString(),
]
);
}
else {
$this->assertNoHelpTexts([$list_text]);
$this->assertNoHelpLink($fieldset, 'media list');
}
}
/**
* Tests the redirect URL after creating a media item.
*/
public function testMediaCreateRedirect() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->createMediaType('test', [
'queue_thumbnail_downloads' => FALSE,
]);
// Test a redirect to the media canonical URL for a user without the 'access
// media overview' permission.
$this->drupalLogin($this->drupalCreateUser([
'view media',
'create media',
]));
$this->drupalGet('media/add');
$page->fillField('name[0][value]', $this->randomMachineName());
$page->fillField('field_media_test[0][value]', $this->randomString());
$page->pressButton('Save');
$media_id = $this->container->get('entity_type.manager')
->getStorage('media')
->getQuery()
->execute();
$media_id = reset($media_id);
$assert_session->addressEquals("media/$media_id/edit");
// Test a redirect to the media overview for a user with the 'access media
// overview' permission.
$this->drupalLogin($this->drupalCreateUser([
'view media',
'create media',
'access media overview',
]));
$this->drupalGet('media/add');
$page->fillField('name[0][value]', $this->randomMachineName());
$page->fillField('field_media_test[0][value]', $this->randomString());
$page->pressButton('Save');
$assert_session->addressEquals('admin/content/media');
}
/**
* Asserts that the given texts are present exactly once.
*
* @param string[] $texts
* A list of the help texts to check.
* @param string $selector
* (optional) The selector to search.
*/
public function assertHelpTexts(array $texts, $selector = '') {
$assert_session = $this->assertSession();
foreach ($texts as $text) {
// We only want to escape single quotes, so use str_replace() rather than
// addslashes().
$text = str_replace("'", "\'", $text);
if ($selector) {
$assert_session->elementsCount('css', $selector . ":contains('$text')", 1);
}
else {
$assert_session->pageTextContains($text);
}
}
}
/**
* Asserts that none of the given texts are present.
*
* @param string[] $texts
* A list of the help texts to check.
*/
public function assertNoHelpTexts(array $texts) {
$assert_session = $this->assertSession();
foreach ($texts as $text) {
$assert_session->pageTextNotContains($text);
}
}
/**
* Asserts whether a given link is present.
*
* @param \Behat\Mink\Element\NodeElement $element
* The element to search.
* @param string $text
* The link text.
* @param string[] $attributes
* An associative array of any expected attributes, keyed by the
* attribute name.
*/
protected function assertHelpLink(NodeElement $element, $text, array $attributes = []) {
// Find all the links inside the element.
$link = $element->findLink($text);
$this->assertNotEmpty($link);
foreach ($attributes as $attribute => $value) {
$this->assertSame($link->getAttribute($attribute), $value);
}
}
/**
* Asserts that a given link is not present.
*
* @param \Behat\Mink\Element\NodeElement $element
* The element to search.
* @param string $text
* The link text.
*/
protected function assertNoHelpLink(NodeElement $element, $text) {
$assert_session = $this->assertSession();
// Assert that the link and its text are not present anywhere on the page.
$assert_session->elementNotExists('named', ['link', $text], $element);
$assert_session->pageTextNotContains($text);
}
/**
* Test the media collection route.
*/
public function testMediaCollectionRoute() {
/** @var \Drupal\Core\Entity\EntityStorageInterface $media_storage */
$media_storage = $this->container->get('entity_type.manager')->getStorage('media');
$this->container->get('module_installer')->uninstall(['views']);
// Create a media type and media item.
$media_type = $this->createMediaType('test');
$media = $media_storage->create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$this->drupalGet($media->toUrl('collection'));
$assert_session = $this->assertSession();
// Media list table exists.
$assert_session->elementExists('css', 'th:contains("Media Name")');
$assert_session->elementExists('css', 'th:contains("Type")');
$assert_session->elementExists('css', 'th:contains("Operations")');
// Media item is present.
$assert_session->elementExists('css', 'td:contains("Unnamed")');
}
}
@@ -0,0 +1,96 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\OEmbed\ProviderException;
/**
* Tests the oEmbed provider repository.
*
* @covers \Drupal\media\OEmbed\ProviderRepository
*
* @group media
*/
class ProviderRepositoryTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests that provider discovery fails if the provider database is empty.
*
* @param string $content
* The expected JSON content of the provider database.
*
* @dataProvider providerEmptyProviderList
*/
public function testEmptyProviderList($content) {
$response = $this->prophesize('\GuzzleHttp\Psr7\Response');
$response->getBody()->willReturn($content);
$client = $this->createMock('\GuzzleHttp\Client');
$client->method('request')->withAnyParameters()->willReturn($response->reveal());
$this->container->set('http_client', $client);
$this->expectException(ProviderException::class);
$this->expectExceptionMessage('Remote oEmbed providers database returned invalid or empty list.');
$this->container->get('media.oembed.provider_repository')->getAll();
}
/**
* Data provider for testEmptyProviderList().
*
* @see ::testEmptyProviderList()
*
* @return array
*/
public function providerEmptyProviderList() {
return [
'empty array' => ['[]'],
'empty string' => [''],
];
}
/**
* Tests that provider discovery fails with a non-existent provider database.
*
* @param string $providers_url
* The URL of the provider database.
* @param string $exception_message
* The expected exception message.
*
* @dataProvider providerNonExistingProviderDatabase
*/
public function testNonExistingProviderDatabase($providers_url, $exception_message) {
$this->config('media.settings')
->set('oembed_providers_url', $providers_url)
->save();
$this->expectException(ProviderException::class);
$this->expectExceptionMessage($exception_message);
$this->container->get('media.oembed.provider_repository')->getAll();
}
/**
* Data provider for testEmptyProviderList().
*
* @see ::testEmptyProviderList()
*
* @return array
*/
public function providerNonExistingProviderDatabase() {
return [
[
'http://oembed1.com/providers.json',
'Could not retrieve the oEmbed provider database from http://oembed1.com/providers.json',
],
[
'http://oembed.com/providers1.json',
'Could not retrieve the oEmbed provider database from http://oembed.com/providers1.json',
],
];
}
}
@@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\OEmbed\Resource;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
/**
* Tests the oEmbed resource fetcher service.
*
* @coversDefaultClass \Drupal\media\OEmbed\ResourceFetcher
*
* @group media
*/
class ResourceFetcherTest extends MediaFunctionalTestBase {
use OEmbedTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->useFixtureProviders();
$this->lockHttpClientToFixtures();
}
/**
* Data provider for testFetchResource().
*
* @return array
*/
public function providerFetchResource() {
return [
'JSON resource' => [
'video_vimeo.json',
'Vimeo',
'Drupal Rap Video - Schipulcon09',
],
'XML resource' => [
'video_collegehumor.xml',
'CollegeHumor',
"Let's Not Get a Drink Sometime",
],
];
}
/**
* Tests resource fetching.
*
* @param string $resource_url
* The URL of the resource to fetch, relative to the base URL.
* @param string $provider_name
* The expected name of the resource provider.
* @param string $title
* The expected title of the resource.
*
* @covers ::fetchResource
*
* @dataProvider providerFetchResource
*/
public function testFetchResource($resource_url, $provider_name, $title) {
/** @var \Drupal\media\OEmbed\Resource $resource */
$resource = $this->container->get('media.oembed.resource_fetcher')
->fetchResource($resource_url);
$this->assertInstanceOf(Resource::class, $resource);
$this->assertSame($provider_name, $resource->getProvider()->getName());
$this->assertSame($title, $resource->getTitle());
}
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class MediaJsonAnonTest extends MediaResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class MediaJsonBasicAuthTest extends MediaResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class MediaJsonCookieTest extends MediaResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,458 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
use GuzzleHttp\RequestOptions;
abstract class MediaResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'media';
/**
* @var \Drupal\media\MediaInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed' => NULL,
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
// Provisioning the Media REST resource without the File REST resource does
// not make sense.
$this->resourceConfigStorage->create([
'id' => 'entity.file',
'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY,
'configuration' => [
'methods' => ['GET'],
'formats' => [static::$format],
'authentication' => isset(static::$auth) ? [static::$auth] : [],
],
'status' => TRUE,
])->save();
$this->container->get('router.builder')->rebuild();
}
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['view media']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['create camelids media', 'access content']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['edit any camelids media']);
// @todo Remove this in https://www.drupal.org/node/2824851.
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['delete any camelids media']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
if (!MediaType::load('camelids')) {
// Create a "Camelids" media type.
$media_type = MediaType::create([
'name' => 'Camelids',
'id' => 'camelids',
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'source' => 'file',
]);
$media_type->save();
// Create the source field.
$source_field = $media_type->getSource()->createSourceField($media_type);
$source_field->getFieldStorageDefinition()->save();
$source_field->save();
$media_type
->set('source_configuration', [
'source_field' => $source_field->getName(),
])
->save();
}
// Create a file to upload.
$file = File::create([
'uri' => 'public://llama.txt',
]);
$file->setPermanent();
$file->save();
// Create a "Llama" media item.
$media = Media::create([
'bundle' => 'camelids',
'field_media_file' => [
'target_id' => $file->id(),
],
]);
$media
->setName('Llama')
->setPublished()
->setCreatedTime(123456789)
->setOwnerId(static::$auth ? $this->account->id() : 0)
->setRevisionUserId(static::$auth ? $this->account->id() : 0)
->save();
return $media;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$file = File::load(1);
$thumbnail = File::load(2);
$author = User::load($this->entity->getOwnerId());
return [
'mid' => [
[
'value' => 1,
],
],
'uuid' => [
[
'value' => $this->entity->uuid(),
],
],
'vid' => [
[
'value' => 1,
],
],
'langcode' => [
[
'value' => 'en',
],
],
'bundle' => [
[
'target_id' => 'camelids',
'target_type' => 'media_type',
'target_uuid' => MediaType::load('camelids')->uuid(),
],
],
'name' => [
[
'value' => 'Llama',
],
],
'field_media_file' => [
[
'description' => NULL,
'display' => NULL,
'target_id' => (int) $file->id(),
'target_type' => 'file',
'target_uuid' => $file->uuid(),
'url' => $file->createFileUrl(FALSE),
],
],
'thumbnail' => [
[
'alt' => '',
'width' => 180,
'height' => 180,
'target_id' => (int) $thumbnail->id(),
'target_type' => 'file',
'target_uuid' => $thumbnail->uuid(),
'title' => NULL,
'url' => $thumbnail->createFileUrl(FALSE),
],
],
'status' => [
[
'value' => TRUE,
],
],
'created' => [
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'revision_created' => [
$this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()),
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'uid' => [
[
'target_id' => (int) $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_user' => [
[
'target_id' => (int) $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_log_message' => [],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'bundle' => [
[
'target_id' => 'camelids',
],
],
'name' => [
[
'value' => 'Dramallama',
],
],
'field_media_file' => [
[
'description' => NULL,
'display' => NULL,
'target_id' => 3,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPatchEntity() {
return array_diff_key($this->getNormalizedPostEntity(), ['field_media_file' => TRUE]);
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
switch ($method) {
case 'GET';
return "The 'view media' permission is required when the media item is published.";
case 'POST':
return "The following permissions are required: 'administer media' OR 'create media' OR 'create camelids media'.";
case 'PATCH':
return "The following permissions are required: 'update any media' OR 'update own media' OR 'camelids: edit any media' OR 'camelids: edit own media'.";
case 'DELETE':
return "The following permissions are required: 'delete any media' OR 'delete own media' OR 'camelids: delete any media' OR 'camelids: delete own media'.";
default:
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
/**
* {@inheritdoc}
*/
public function testPost() {
$file_storage = $this->container->get('entity_type.manager')->getStorage('file');
// Step 1: upload file, results in File entity marked temporary.
$this->uploadFile();
$file = $file_storage->loadUnchanged(3);
$this->assertTrue($file->isTemporary());
$this->assertFalse($file->isPermanent());
// Step 2: create Media entity using the File, makes File entity permanent.
parent::testPost();
$file = $file_storage->loadUnchanged(3);
$this->assertFalse($file->isTemporary());
$this->assertTrue($file->isPermanent());
}
/**
* This duplicates some of the 'file_upload' REST resource plugin test
* coverage, to be able to test it on a concrete use case.
*/
protected function uploadFile() {
// Enable the 'file_upload' REST resource for the current format + auth.
$this->resourceConfigStorage->create([
'id' => 'file.upload',
'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY,
'configuration' => [
'methods' => ['POST'],
'formats' => [static::$format],
'authentication' => isset(static::$auth) ? [static::$auth] : [],
],
'status' => TRUE,
])->save();
$this->refreshTestStateAfterRestConfigChange();
$this->initAuthentication();
// POST to create a File entity.
$url = Url::fromUri('base:file/upload/media/camelids/field_media_file');
$url->setOption('query', ['_format' => static::$format]);
$request_options = [];
$request_options[RequestOptions::HEADERS] = [
// Set the required (and only accepted) content type for the request.
'Content-Type' => 'application/octet-stream',
// Set the required Content-Disposition header for the file name.
'Content-Disposition' => 'file; filename="drupal rocks 🤘.txt"',
];
$request_options[RequestOptions::BODY] = 'Drupal is the best!';
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions('POST'));
$response = $this->request('POST', $url, $request_options);
$this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('POST'), $response);
// Grant necessary permission, retry.
$this->grantPermissionsToTestedRole(['create camelids media']);
$response = $this->request('POST', $url, $request_options);
$this->assertSame(201, $response->getStatusCode());
$expected = $this->getExpectedNormalizedFileEntity();
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
// To still run the complete test coverage for POSTing a Media entity, we
// must revoke the additional permissions that we granted.
$role = Role::load(static::$auth ? RoleInterface::AUTHENTICATED_ID : RoleInterface::ANONYMOUS_ID);
$role->revokePermission('create camelids media');
$role->trustData()->save();
}
/**
* Gets the expected file entity.
*
* @return array
* The expected normalized data array.
*/
protected function getExpectedNormalizedFileEntity() {
$file = File::load(3);
$owner = static::$auth ? $this->account : User::load(0);
return [
'fid' => [
[
'value' => 3,
],
],
'uuid' => [
[
'value' => $file->uuid(),
],
],
'langcode' => [
[
'value' => 'en',
],
],
'uid' => [
[
'target_id' => (int) $owner->id(),
'target_type' => 'user',
'target_uuid' => $owner->uuid(),
'url' => base_path() . 'user/' . $owner->id(),
],
],
'filename' => [
[
'value' => 'drupal rocks 🤘.txt',
],
],
'uri' => [
[
'value' => 'public://' . date('Y-m') . '/drupal rocks 🤘.txt',
'url' => base_path() . $this->siteDirectory . '/files/' . date('Y-m') . '/drupal%20rocks%20%F0%9F%A4%98.txt',
],
],
'filemime' => [
[
'value' => 'text/plain',
],
],
'filesize' => [
[
'value' => 19,
],
],
'status' => [
[
'value' => FALSE,
],
],
'created' => [
$this->formatExpectedTimestampItemValues($file->getCreatedTime()),
],
'changed' => [
$this->formatExpectedTimestampItemValues($file->getChangedTime()),
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) {
// @see \Drupal\media\MediaAccessControlHandler::checkAccess()
return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated)
->addCacheTags(['media:1']);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonAnonTest extends MediaTypeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonBasicAuthTest extends MediaTypeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonCookieTest extends MediaTypeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,78 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\media\Entity\MediaType;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class MediaTypeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'media_type';
/**
* @var \Drupal\media\MediaTypeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer media types']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" media type.
$camelids = MediaType::create([
'name' => 'Camelids',
'id' => 'camelids',
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'source' => 'file',
]);
$camelids->save();
return $camelids;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'field_map' => [],
'id' => 'camelids',
'label' => NULL,
'langcode' => 'en',
'new_revision' => FALSE,
'queue_thumbnail_downloads' => FALSE,
'source' => 'file',
'source_configuration' => [
'source_field' => '',
],
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaTypeXmlAnonTest extends MediaTypeResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,41 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaTypeXmlBasicAuthTest extends MediaTypeResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaTypeXmlCookieTest extends MediaTypeResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaXmlAnonTest extends MediaResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,41 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaXmlBasicAuthTest extends MediaResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\media\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class MediaXmlCookieTest extends MediaResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}
@@ -0,0 +1,138 @@
<?php
namespace Drupal\Tests\media\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\media\Entity\Media;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\user\Entity\Role;
/**
* Tests that media settings are properly updated during database updates.
*
* @group media
* @group legacy
*/
class MediaUpdateTest extends UpdatePathTestBase {
use MediaTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.4.0-media_installed.php',
__DIR__ . '/../../../fixtures/update/drupal-8.media-add-additional-permissions.php',
];
}
/**
* Tests that media permissions are correctly migrated.
*
* @see media_update_8500()
*/
public function testBundlePermission() {
$this->runUpdates();
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(Role::AUTHENTICATED_ID);
$media_types = \Drupal::entityQuery('media_type')->execute();
foreach ($media_types as $media_type) {
$this->assertTrue($role->hasPermission("create $media_type media"));
$this->assertTrue($role->hasPermission("edit own $media_type media"));
$this->assertTrue($role->hasPermission("edit any $media_type media"));
$this->assertTrue($role->hasPermission("delete own $media_type media"));
$this->assertTrue($role->hasPermission("delete any $media_type media"));
}
}
/**
* Tests that media.settings config is updated with oEmbed configuration.
*
* @see media_update_8600()
*/
public function testOEmbedConfig() {
$config = $this->config('media.settings');
$this->assertNull($config->get('oembed_providers_url'));
$this->assertNull($config->get('iframe_domain'));
$this->runUpdates();
$config = $this->config('media.settings');
$this->assertSame('https://oembed.com/providers.json', $config->get('oembed_providers_url'));
$this->assertSame('', $config->get('iframe_domain'));
}
/**
* Tests that the media entity type has an 'owner' entity key.
*
* @see media_update_8700()
*/
public function testOwnerEntityKey() {
// Check that the 'owner' entity key does not exist prior to the update.
$entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('media');
$this->assertFalse($entity_type->getKey('owner'));
// Run updates.
$this->runUpdates();
// Check that the entity key exists and it has the correct value.
$entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('media');
$this->assertEquals('uid', $entity_type->getKey('owner'));
}
/**
* Tests that the standalone URL is still accessible.
*
* @see media_post_update_enable_standalone_url()
*/
public function testEnableStandaloneUrl() {
$this->container->get('module_installer')->install(['media_test_source']);
// Create a media type.
$media_type = $this->createMediaType('test');
// Run updates.
$this->runUpdates();
// Create a media item.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$user = $this->drupalCreateUser([
'administer media',
]);
$this->drupalLogin($user);
$this->drupalGet('media/' . $media->id());
$this->assertSession()->statusCodeEquals(200);
}
/**
* Tests that the status extra filter is added to the media view.
*
* @see media_post_update_add_status_extra_filter()
*/
public function testMediaViewStatusExtraFilter() {
$config = $this->config('views.view.media');
$this->assertNull($config->get('display.default.display_options.filters.status_extra'));
$this->runUpdates();
$config = $this->config('views.view.media');
$filter = $config->get('display.default.display_options.filters.status_extra');
$this->assertInternalType('array', $filter);
$this->assertSame('status_extra', $filter['field']);
$this->assertSame('media', $filter['entity_type']);
$this->assertSame('media_status', $filter['plugin_id']);
$this->assertSame('status_extra', $filter['id']);
$this->assertFalse($filter['exposed']);
}
}
@@ -0,0 +1,138 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
/**
* Tests the oEmbed URL resolver service.
*
* @coversDefaultClass \Drupal\media\OEmbed\UrlResolver
*
* @group media
*/
class UrlResolverTest extends MediaFunctionalTestBase {
use OEmbedTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->lockHttpClientToFixtures();
$this->useFixtureProviders();
}
/**
* Data provider for testEndpointMatching().
*
* @see ::testEndpointMatching()
*
* @return array
*/
public function providerEndpointMatching() {
return [
'match by endpoint: Twitter' => [
'https://twitter.com/Dries/status/999985431595880448',
'https://publish.twitter.com/oembed?url=https://twitter.com/Dries/status/999985431595880448',
],
'match by endpoint: Vimeo' => [
'https://vimeo.com/14782834',
'https://vimeo.com/api/oembed.json?url=https://vimeo.com/14782834',
],
'match by endpoint: CollegeHumor' => [
'http://www.collegehumor.com/video/40002870/lets-not-get-a-drink-sometime',
'http://www.collegehumor.com/oembed.json?url=http://www.collegehumor.com/video/40002870/lets-not-get-a-drink-sometime',
],
];
}
/**
* Tests resource URL resolution when the asset URL can be matched to a
* provider endpoint.
*
* @covers ::getProviderByUrl
* @covers ::getResourceUrl
*
* @param string $url
* The asset URL to resolve.
* @param string $resource_url
* The expected oEmbed resource URL of the asset.
*
* @dataProvider providerEndpointMatching
*/
public function testEndpointMatching($url, $resource_url) {
$this->assertSame(
$resource_url,
$this->container->get('media.oembed.url_resolver')->getResourceUrl($url)
);
}
/**
* Tests that hook_oembed_resource_url_alter() is invoked.
*
* @depends testEndpointMatching
*/
public function testResourceUrlAlterHook() {
$this->container->get('module_installer')->install(['media_test_oembed']);
$resource_url = $this->container->get('media.oembed.url_resolver')
->getResourceUrl('https://vimeo.com/14782834');
$this->assertStringContainsString('altered=1', parse_url($resource_url, PHP_URL_QUERY));
}
/**
* Data provider for testUrlDiscovery().
*
* @see ::testUrlDiscovery()
*
* @return array
*/
public function providerUrlDiscovery() {
return [
'JSON resource' => [
'video_vimeo.html',
'https://vimeo.com/api/oembed.json?url=video_vimeo.html',
],
'XML resource' => [
'video_collegehumor.html',
// The endpoint does not explicitly declare that it supports XML, so
// only JSON support is assumed, which is why the discovered URL
// contains '.json'. However, the fetched HTML file contains a
// relationship to an XML representation of the resource, with the
// application/xml+oembed MIME type.
'http://www.collegehumor.com/oembed.json?url=video_collegehumor.html',
],
];
}
/**
* Tests URL resolution when the resource URL must be actively discovered by
* scanning the asset.
*
* @param string $url
* The asset URL to resolve.
* @param string $resource_url
* The expected oEmbed resource URL of the asset.
*
* @covers ::discoverResourceUrl
* @covers ::getProviderByUrl
* @covers ::getResourceUrl
*
* @dataProvider providerUrlDiscovery
*/
public function testUrlDiscovery($url, $resource_url) {
$this->assertSame(
$this->container->get('media.oembed.url_resolver')->getResourceUrl($url),
$resource_url
);
}
}
@@ -0,0 +1,203 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\media\Entity\Media;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Basic display tests for Media.
*
* @group media
*/
class MediaDisplayTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Install the optional configs from the standard profile.
$extension_path = drupal_get_path('profile', 'standard');
$optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
$storage = new FileStorage($optional_install_path);
$this->container->get('config.installer')->installOptionalConfig($storage, '');
// Reset all the static caches and list caches.
$this->container->get('config.factory')->reset();
// This test is going to test the display, so we need the standalone URL.
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
}
/**
* Test basic media display.
*/
public function testMediaDisplay() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$media_type = $this->createMediaType('test');
// Create a media item.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Fantastic!',
]);
$media->save();
$this->drupalGet('media/' . $media->id());
// Verify the "name" field is really not present.
$assert_session->elementNotExists('css', '.field--name-name');
// Enable the field on the display and verify it becomes visible on the UI.
$this->drupalGet("/admin/structure/media/manage/{$media_type->id()}/display");
$assert_session->buttonExists('Show row weights')->press();
$this->assertSession()->waitForElementVisible('css', '[name="fields[name][region]"]');
$page->selectFieldOption('fields[name][region]', 'content');
$assert_session->waitForElementVisible('css', '#edit-fields-name-settings-edit');
$page->pressButton('Save');
$this->drupalGet('media/' . $media->id());
// Verify the name is present, and its text matches what is expected.
$assert_session->elementExists('css', '.field--name-name');
$name_field = $page->find('css', '.field--name-name .field__item');
$this->assertSame($media->label(), $name_field->getText());
// In the standard profile, there are some pre-cooked types. Make sure the
// elements configured on their displays are the expected ones.
$this->drupalGet('media/add/image');
$image_media_name = 'example_1.jpeg';
$page->attachFileToField('files[field_media_image_0]', $this->root . '/core/modules/media/tests/fixtures/' . $image_media_name);
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->fillField('field_media_image[0][alt]', 'Image Alt Text 1');
$page->pressButton('Save');
$image_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$image_media_id = reset($image_media_id);
// Go to the media entity view.
$this->drupalGet('/media/' . $image_media_id);
// Check if the default media name is generated as expected.
$assert_session->elementTextContains('css', 'h1', $image_media_name);
// Here we expect to see only the image, nothing else.
// Assert only one element in the content region.
$this->assertCount(1, $page->findAll('css', '.media--type-image > div'));
// Assert the image is present inside the media element.
$media_item = $assert_session->elementExists('css', '.media--type-image > div');
$assert_session->elementExists('css', 'img', $media_item);
// Assert that the image src uses the large image style, the label is
// visually hidden, and there is no link to the image file.
$media_image = $assert_session->elementExists('css', '.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg')));
$this->assertStringContainsString($expected_image_src, $media_image->getAttribute('src'));
$field = $assert_session->elementExists('css', '.field--name-field-media-image');
$assert_session->elementExists('css', '.field__label.visually-hidden', $field);
$assert_session->elementNotExists('css', 'a', $field);
$test_filename = $this->randomMachineName() . '.txt';
$test_filepath = 'public://' . $test_filename;
file_put_contents($test_filepath, $this->randomMachineName());
$this->drupalGet("media/add/document");
$page->attachFileToField("files[field_media_document_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
// Go to the media entity view.
$this->drupalGet($this->assertLinkToCreatedMedia());
// Check if the default media name is generated as expected.
$assert_session->elementTextContains('css', 'h1', $test_filename);
// Here we expect to see only the linked filename.
// Assert only one element in the content region.
$this->assertCount(1, $page->findAll('css', 'article.media--type-document > div'));
// Assert the file link is present, and its text matches the filename.
$assert_session->elementExists('css', 'article.media--type-document .field--name-field-media-document a');
$link = $page->find('css', 'article.media--type-document .field--name-field-media-document a');
$this->assertSame($test_filename, $link->getText());
// Create a node type "page" to use as host entity.
$node_type = NodeType::create([
'type' => 'page',
'name' => 'Page',
]);
$node_type->save();
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$storage = FieldStorageConfig::create([
'entity_type' => 'node',
'field_name' => 'field_related_media',
'type' => 'entity_reference',
'settings' => [
'target_type' => 'media',
],
]);
$storage->save();
FieldConfig::create([
'field_storage' => $storage,
'entity_type' => 'node',
'bundle' => $node_type->id(),
'label' => 'Related media',
'settings' => [
'handler_settings' => [
'target_bundles' => [
'image' => 'image',
],
],
],
])->save();
\Drupal::service('entity_display.repository')->getViewDisplay('node', $node_type->id())
->setComponent('field_related_media', [
'type' => 'entity_reference_entity_view',
'label' => 'hidden',
'settings' => [
'view_mode' => 'full',
],
])->save();
$node = Node::create([
'title' => 'Host node',
'type' => $node_type->id(),
'field_related_media' => [
'target_id' => $image_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Media field is there.
$assert_session->elementExists('css', '.field--name-field-related-media');
// Media name element is not there.
$assert_session->elementNotExists('css', '.field--name-name');
$assert_session->pageTextNotContains($image_media_name);
// Only one element is present inside the media container.
$this->assertCount(1, $page->findAll('css', '.field--name-field-related-media article.media--type-image > div'));
// Assert the image is present.
$assert_session->elementExists('css', '.field--name-field-related-media article.media--type-image img');
}
}
@@ -0,0 +1,262 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\filter\Entity\FilterFormat;
/**
* @covers ::media_filter_format_edit_form_validate
* @group media
*/
class MediaEmbedFilterConfigurationUiTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
// Necessary for @covers to work.
require_once __DIR__ . '/../../../media.module';
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$format = FilterFormat::create([
'format' => 'media_embed_test',
'name' => 'Test format',
'filters' => [],
]);
$format->save();
$this->drupalLogin($this->drupalCreateUser([
'administer filters',
$format->getPermissionName(),
]));
}
/**
* @covers ::media_form_filter_format_add_form_alter
* @covers ::media_filter_format_edit_form_validate
* @dataProvider providerTestValidations
*/
public function testValidationWhenAdding($filter_html_status, $filter_align_status, $filter_caption_status, $filter_html_image_secure_status, $media_embed, $allowed_html, $expected_error_message) {
$this->drupalGet('admin/config/content/formats/add');
// Enable the `filter_html` and `media_embed` filters.
$page = $this->getSession()->getPage();
$page->fillField('name', 'Another test format');
$this->showHiddenFields();
$page->findField('format')->setValue('another_media_embed_test');
if ($filter_html_status) {
$page->checkField('filters[filter_html][status]');
}
if ($filter_align_status) {
$page->checkField('filters[filter_align][status]');
}
if ($filter_caption_status) {
$page->checkField('filters[filter_caption][status]');
}
if ($filter_html_image_secure_status) {
$page->checkField('filters[filter_html_image_secure][status]');
}
if ($media_embed === TRUE || is_numeric($media_embed)) {
$page->checkField('filters[media_embed][status]');
// Set a non-default weight.
if (is_numeric($media_embed)) {
$this->click('.tabledrag-toggle-weight');
$page->selectFieldOption('filters[media_embed][weight]', $media_embed);
}
}
if (!empty($allowed_html)) {
$page->clickLink('Limit allowed HTML tags and correct faulty HTML');
$page->fillField('filters[filter_html][settings][allowed_html]', $allowed_html);
}
$page->pressButton('Save configuration');
if ($expected_error_message) {
$this->assertSession()->pageTextNotContains('Added text format Another test format.');
$this->assertSession()->pageTextContains($expected_error_message);
}
else {
$this->assertSession()->pageTextContains('Added text format Another test format.');
}
}
/**
* @covers ::media_form_filter_format_edit_form_alter
* @covers ::media_filter_format_edit_form_validate
* @dataProvider providerTestValidations
*/
public function testValidationWhenEditing($filter_html_status, $filter_align_status, $filter_caption_status, $filter_html_image_secure_status, $media_embed, $allowed_html, $expected_error_message) {
$this->drupalGet('admin/config/content/formats/manage/media_embed_test');
// Enable the `filter_html` and `media_embed` filters.
$page = $this->getSession()->getPage();
if ($filter_html_status) {
$page->checkField('filters[filter_html][status]');
}
if ($filter_align_status) {
$page->checkField('filters[filter_align][status]');
}
if ($filter_caption_status) {
$page->checkField('filters[filter_caption][status]');
}
if ($filter_html_image_secure_status) {
$page->checkField('filters[filter_html_image_secure][status]');
}
if ($media_embed === TRUE || is_numeric($media_embed)) {
$page->checkField('filters[media_embed][status]');
// Set a non-default weight.
if (is_numeric($media_embed)) {
$this->click('.tabledrag-toggle-weight');
$page->selectFieldOption('filters[media_embed][weight]', $media_embed);
}
}
if (!empty($allowed_html)) {
$page->clickLink('Limit allowed HTML tags and correct faulty HTML');
$page->fillField('filters[filter_html][settings][allowed_html]', $allowed_html);
}
$page->pressButton('Save configuration');
if ($expected_error_message) {
$this->assertSession()->pageTextNotContains('The text format Test format has been updated.');
$this->assertSession()->pageTextContains($expected_error_message);
}
else {
$this->assertSession()->pageTextContains('The text format Test format has been updated.');
}
}
/**
* Data provider for testValidationWhenAdding() and
* testValidationWhenEditing().
*/
public function providerTestValidations() {
return [
'Tests that no filter_html occurs when filter_html not enabled.' => [
'filters[filter_html][status]' => FALSE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => TRUE,
'allowed_html' => FALSE,
'expected_error_message' => FALSE,
],
'Tests validation when both filter_html and media_embed are disabled.' => [
'filters[filter_html][status]' => FALSE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => FALSE,
'allowed_html' => FALSE,
'expected_error_message' => FALSE,
],
'Tests validation when media_embed filter not enabled and filter_html is enabled.' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => FALSE,
'allowed_html' => 'default',
'expected_error_message' => FALSE,
],
'Tests validation when drupal-media element has no attributes.' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => TRUE,
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media>",
'expected_error_message' => 'The <drupal-media> tag in the allowed HTML tags is missing the following attributes: data-entity-type, data-entity-uuid.',
],
'Tests validation when drupal-media element lacks some required attributes.' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => TRUE,
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-uuid data-align>",
'expected_error_message' => 'The <drupal-media> tag in the allowed HTML tags is missing the following attributes: data-entity-type.',
],
'Tests validation when both filter_html and media_embed are enabled and configured correctly' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => TRUE,
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-type data-entity-uuid data-view-mode>",
'expected_error_message' => FALSE,
],
'Order validation: media_embed before all filters' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => TRUE,
'filters[filter_caption][status]' => TRUE,
'filters[filter_html_image_secure][status]' => TRUE,
'media_embed' => -5,
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-type data-entity-uuid data-view-mode>",
'expected_error_message' => 'The Embed media filter needs to be placed after the following filters: Align images, Caption images, Restrict images to this site.',
],
'Order validation: media_embed before filter_align' => [
'filters[filter_html][status]' => FALSE,
'filters[filter_align][status]' => TRUE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => -5,
'allowed_html' => '',
'expected_error_message' => 'The Embed media filter needs to be placed after the Align images filter.',
],
'Order validation: media_embed before filter_caption' => [
'filters[filter_html][status]' => FALSE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => TRUE,
'filters[filter_html_image_secure][status]' => FALSE,
'media_embed' => -5,
'allowed_html' => '',
'expected_error_message' => 'The Embed media filter needs to be placed after the Caption images filter.',
],
'Order validation: media_embed before filter_html_image_secure' => [
'filters[filter_html][status]' => FALSE,
'filters[filter_align][status]' => FALSE,
'filters[filter_caption][status]' => FALSE,
'filters[filter_html_image_secure][status]' => TRUE,
'media_embed' => -5,
'allowed_html' => '',
'expected_error_message' => 'The Embed media filter needs to be placed after the Restrict images to this site filter.',
],
'Order validation: media_embed after filter_align and filter_caption but before filter_html_image_secure' => [
'filters[filter_html][status]' => TRUE,
'filters[filter_align][status]' => TRUE,
'filters[filter_caption][status]' => TRUE,
'filters[filter_html_image_secure][status]' => TRUE,
'media_embed' => 5,
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-type data-entity-uuid data-view-mode>",
'expected_error_message' => 'The Embed media filter needs to be placed after the Restrict images to this site filter.',
],
];
}
/**
* Show visually hidden fields.
*/
protected function showHiddenFields() {
$script = <<<JS
var hidden_fields = document.querySelectorAll(".visually-hidden");
[].forEach.call(hidden_fields, function(el) {
el.classList.remove("visually-hidden");
});
JS;
$this->getSession()->executeScript($script);
}
}
@@ -0,0 +1,63 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\media\Functional\MediaFunctionalTestTrait;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
/**
* Base class for Media functional JavaScript tests.
*/
abstract class MediaJavascriptTestBase extends WebDriverTestBase {
use MediaFunctionalTestTrait;
use MediaTypeCreationTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'system',
'node',
'field_ui',
'views_ui',
'media',
'media_test_source',
];
/**
* Waits and asserts that a given element is visible.
*
* @param string $selector
* The CSS selector.
* @param int $timeout
* (Optional) Timeout in milliseconds, defaults to 1000.
* @param string $message
* (Optional) Message to pass to assertJsCondition().
*/
protected function waitUntilVisible($selector, $timeout = 1000, $message = '') {
$condition = "jQuery('" . $selector . ":visible').length > 0";
$this->assertJsCondition($condition, $timeout, $message);
}
/**
* Asserts that a link to a new media item is displayed in the messages area.
*
* @return string
* The link URL.
*/
protected function assertLinkToCreatedMedia() {
$assert_session = $this->assertSession();
$selector = '.messages a';
// Get the canonical media entity URL from the creation message.
$link = $assert_session->elementExists('css', $selector);
$assert_session->elementAttributeExists('css', $selector, 'href');
return $link->getAttribute('href');
}
}
@@ -0,0 +1,61 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Component\Utility\Html;
/**
* Tests related to media reference fields.
*
* @group media
*/
class MediaReferenceFieldHelpTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Test our custom help texts when creating a field.
*
* @see media_form_field_ui_field_storage_add_form_alter()
*/
public function testFieldCreationHelpText() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$type = $this->drupalCreateContentType([
'type' => 'foo',
]);
$this->drupalGet("/admin/structure/types/manage/{$type->id()}/fields/add-field");
$field_types = [
'file',
'image',
'field_ui:entity_reference:media',
];
$description_ids = array_map(function ($item) {
return '#edit-description-' . Html::cleanCssIdentifier($item);
}, $field_types);
// Choose a boolean field, none of the description containers should be
// visible.
$assert_session->optionExists('edit-new-storage-type', 'boolean');
$page->selectFieldOption('edit-new-storage-type', 'boolean');
foreach ($description_ids as $description_id) {
$this->assertFalse($assert_session->elementExists('css', $description_id)->isVisible());
}
// Select each of the file, image, and media fields and verify their
// descriptions are now visible and match the expected text.
$help_text = 'Use Media reference fields for most files, images, audio, videos, and remote media. Use File or Image reference fields when creating your own media types, or for legacy files and images created before enabling the Media module.';
foreach ($field_types as $field_name) {
$assert_session->optionExists('edit-new-storage-type', $field_name);
$page->selectFieldOption('edit-new-storage-type', $field_name);
$field_description_element = $assert_session->elementExists('css', '#edit-description-' . Html::cleanCssIdentifier($field_name));
$this->assertTrue($field_description_element->isVisible());
$this->assertSame($help_text, $field_description_element->getText());
}
}
}
@@ -0,0 +1,120 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\file\Entity\File;
/**
* Tests the Audio and Video media sources.
*
* @group media
*/
class MediaSourceAudioVideoTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* Check the Audio source functionality.
*/
public function testAudioTypeCreation() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_id = 'audio_file';
$type_name = 'audio_type';
$field_name = 'field_media_' . $source_id;
$this->doTestCreateMediaType($type_name, $source_id);
// Check that the source field was created with the correct settings.
$storage = FieldStorageConfig::load("media.$field_name");
$this->assertInstanceOf(FieldStorageConfig::class, $storage);
$field = FieldConfig::load("media.$type_name.$field_name");
$this->assertInstanceOf(FieldConfig::class, $field);
$this->assertSame('mp3 wav aac', FieldConfig::load("media.$type_name.$field_name")->get('settings')['file_extensions']);
// Check that the display holds the correct formatter configuration.
$display = EntityViewDisplay::load("media.$type_name.default");
$this->assertInstanceOf(EntityViewDisplay::class, $display);
$formatter = $display->getComponent($field_name)['type'];
$this->assertSame('file_audio', $formatter);
// Create a media asset.
file_put_contents('public://file.mp3', str_repeat('t', 10));
$file = File::create([
'uri' => 'public://file.mp3',
'filename' => 'file.mp3',
]);
$file->save();
$this->drupalGet("media/add/$type_name");
$page->fillField('Name', 'Audio media asset');
$page->attachFileToField("files[{$field_name}_0]", \Drupal::service('file_system')->realpath('public://file.mp3'));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
// Verify that there is a creation message and that it contains a link to
// the media entity.
$assert_session->pageTextContains("$type_name Audio media asset has been created.");
$this->drupalGet($this->assertLinkToCreatedMedia());
// Verify that the <audio> tag is present on the media entity view.
$assert_session->elementExists('css', "audio > source[type='audio/mpeg']");
}
/**
* Check the Video source functionality.
*/
public function testVideoTypeCreation() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_id = 'video_file';
$type_name = 'video_type';
$field_name = 'field_media_' . $source_id;
$this->doTestCreateMediaType($type_name, $source_id);
// Check that the source field was created with the correct settings.
$storage = FieldStorageConfig::load("media.$field_name");
$this->assertInstanceOf(FieldStorageConfig::class, $storage);
$field = FieldConfig::load("media.$type_name.$field_name");
$this->assertInstanceOf(FieldConfig::class, $field);
$this->assertSame('mp4', FieldConfig::load("media.$type_name.$field_name")->getSetting('file_extensions'));
// Check that the display holds the correct formatter configuration.
$display = EntityViewDisplay::load("media.$type_name.default");
$this->assertInstanceOf(EntityViewDisplay::class, $display);
$formatter = $display->getComponent($field_name)['type'];
$this->assertSame('file_video', $formatter);
// Create a media asset.
file_put_contents('public://file.mp4', str_repeat('t', 10));
$file = File::create([
'uri' => 'public://file.mp4',
'filename' => 'file.mp4',
]);
$file->save();
$this->drupalGet("media/add/$type_name");
$page->fillField('Name', 'Video media asset');
$page->attachFileToField("files[{$field_name}_0]", \Drupal::service('file_system')->realpath('public://file.mp4'));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
// Verify that there is a creation message and that it contains a link to
// the media entity.
$assert_session->pageTextContains("$type_name Video media asset has been created.");
$this->drupalGet($this->assertLinkToCreatedMedia());
// Verify that the <video> tag is present on the media entity view.
$assert_session->elementExists('css', "video > source[type='video/mp4']");
}
}
@@ -0,0 +1,116 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\media\Entity\Media;
use Drupal\media\Plugin\media\Source\File;
/**
* Tests the file media source.
*
* @group media
*/
class MediaSourceFileTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* Tests the file media source.
*/
public function testMediaFileSource() {
$media_type_id = 'test_media_file_type';
$source_field_id = 'field_media_file';
$provided_fields = [
File::METADATA_ATTRIBUTE_NAME,
File::METADATA_ATTRIBUTE_SIZE,
File::METADATA_ATTRIBUTE_MIME,
];
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->doTestCreateMediaType($media_type_id, 'file', $provided_fields);
// Create custom fields for the media type to store metadata attributes.
$fields = [
'field_string_file_size' => 'string',
'field_string_mime_type' => 'string',
];
$this->createMediaTypeFields($fields, $media_type_id);
// Hide the name field widget to test default name generation.
$this->hideMediaTypeFieldWidget('name', $media_type_id);
$this->drupalGet("admin/structure/media/manage/{$media_type_id}");
$page->selectFieldOption("field_map[" . File::METADATA_ATTRIBUTE_NAME . "]", 'name');
$page->selectFieldOption("field_map[" . File::METADATA_ATTRIBUTE_SIZE . "]", 'field_string_file_size');
$page->selectFieldOption("field_map[" . File::METADATA_ATTRIBUTE_MIME . "]", 'field_string_mime_type');
$page->pressButton('Save');
$test_filename = $this->randomMachineName() . '.txt';
$test_filepath = 'public://' . $test_filename;
file_put_contents($test_filepath, $this->randomMachineName());
// Create a media item.
$this->drupalGet("media/add/{$media_type_id}");
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$assert_session->addressEquals('admin/content/media');
// Get the media entity view URL from the creation message.
$this->drupalGet($this->assertLinkToCreatedMedia());
// Make sure a link to the file is displayed.
$assert_session->linkExists($test_filename);
// The thumbnail should not be displayed.
$assert_session->elementNotExists('css', '.image-style-thumbnail');
// Make sure checkbox changes the visibility of log message field.
$this->drupalGet("media/1/edit");
$page->uncheckField('revision');
$assert_session->elementAttributeContains('css', '.field--name-revision-log-message', 'style', 'display: none');
$page->checkField('revision');
$assert_session->elementAttributeNotContains('css', '.field--name-revision-log-message', 'style', 'display');
// Load the media and check that all the fields are properly populated.
$media = Media::load(1);
$this->assertSame($test_filename, $media->getName());
$this->assertSame('8', $media->get('field_string_file_size')->value);
$this->assertSame('text/plain', $media->get('field_string_mime_type')->value);
// Test the MIME type icon.
$icon_base = \Drupal::config('media.settings')->get('icon_base_uri');
\Drupal::service('file_system')->copy($icon_base . '/generic.png', $icon_base . '/text--plain.png');
$this->drupalGet("media/add/{$media_type_id}");
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$assert_session->elementAttributeContains('css', '.image-style-thumbnail', 'src', 'text--plain.png');
// Check if the mapped name is automatically updated.
$new_filename = $this->randomMachineName() . '.txt';
$new_filepath = 'public://' . $new_filename;
file_put_contents($new_filepath, $this->randomMachineName());
$this->drupalGet("media/1/edit");
$page->pressButton('Remove');
$result = $assert_session->waitForField("files[{$source_field_id}_0]");
$this->assertNotEmpty($result);
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($new_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
/** @var \Drupal\media\MediaInterface $media */
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged(1);
$this->assertEquals($new_filename, $media->getName());
$assert_session->pageTextContains("$new_filename has been updated.");
}
}
@@ -0,0 +1,186 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media\Plugin\media\Source\Image;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the image media source.
*
* @group media
*/
class MediaSourceImageTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* Tests the image media source.
*/
public function testMediaImageSource() {
$media_type_id = 'test_media_image_type';
$source_field_id = 'field_media_image';
$provided_fields = [
Image::METADATA_ATTRIBUTE_WIDTH,
Image::METADATA_ATTRIBUTE_HEIGHT,
];
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->doTestCreateMediaType($media_type_id, 'image', $provided_fields);
// Create custom fields for the media type to store metadata attributes.
$fields = [
'field_string_width' => 'string',
'field_string_height' => 'string',
];
$this->createMediaTypeFields($fields, $media_type_id);
// Hide the name field widget to test default name generation.
$this->hideMediaTypeFieldWidget('name', $media_type_id);
$this->drupalGet("admin/structure/media/manage/{$media_type_id}");
$page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_WIDTH . "]", 'field_string_width');
$page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_HEIGHT . "]", 'field_string_height');
$page->pressButton('Save');
// Create a media item.
$this->drupalGet("media/add/{$media_type_id}");
$page->attachFileToField("files[{$source_field_id}_0]", $this->root . '/core/modules/media/tests/fixtures/example_1.jpeg');
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->fillField("{$source_field_id}[0][alt]", 'Image Alt Text 1');
$page->pressButton('Save');
$assert_session->addressEquals('admin/content/media');
// Get the media entity view URL from the creation message.
$this->drupalGet($this->assertLinkToCreatedMedia());
// Assert the image element is present inside the media element and that its
// src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file.
$image_element = $assert_session->elementExists('css', '.field--name-field-media-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg')));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$field = $assert_session->elementExists('css', '.field--name-field-media-image');
$assert_session->elementExists('css', '.field__label.visually-hidden', $field);
$assert_session->elementNotExists('css', 'a', $field);
// Ensure the image has the correct alt attribute.
$this->assertSame('Image Alt Text 1', $image_element->getAttribute('alt'));
// Load the media and check that all fields are properly populated.
$media = Media::load(1);
$this->assertSame('example_1.jpeg', $media->getName());
$this->assertSame('200', $media->get('field_string_width')->value);
$this->assertSame('89', $media->get('field_string_height')->value);
// Tests the warning when the default display's image style is missing.
$this->drupalLogin($this->drupalCreateUser([
'administer site configuration',
'access media overview',
'administer media',
'administer media types',
'administer media display',
'view media',
// We need 'access content' for system.machine_name_transliterate.
'access content',
]));
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// If for some reason a site builder deletes the 'large' image style, do
// not add an image style to the new entity view display's image field.
// Instead, add a warning on the 'Status report' page.
ImageStyle::load('large')->delete();
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', 'Madame Bonacieux');
$this->assertNotEmpty($assert_session->waitForText('Machine name: madame_bonacieux'));
$page->selectFieldOption('source', 'image');
// Wait for the form to complete with AJAX.
$this->assertNotEmpty($assert_session->waitForText('Field mapping'));
$page->pressButton('Save');
$this->assertViewDisplayConfigured('madame_bonacieux');
// Create user without the 'administer media display' permission.
$this->drupalLogin($this->drupalCreateUser([
'administer site configuration',
'access media overview',
'administer media',
'administer media types',
'view media',
// We need 'access content' for system.machine_name_transliterate.
'access content',
]));
// Test that hook_requirements adds warning about the lack of an image
// style.
$this->drupalGet('/admin/reports/status');
// The image style warning should not include an action link when the
// current user lacks the permission 'administer media display'.
$assert_session->pageTextContains('The default display for the Madame Bonacieux media type is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads.');
$assert_session->linkNotExists('add an image style to the Image field');
$assert_session->linkByHrefNotExists('/admin/structure/media/manage/madame_bonacieux/display');
// The image style warning should include an action link when the current
// user has the permission 'administer media display'.
Role::load(RoleInterface::AUTHENTICATED_ID)
->grantPermission('administer media display')
->save();
$this->drupalGet('/admin/reports/status');
$assert_session->pageTextContains('The default display for the Madame Bonacieux media type is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads. If you would like to change this, add an image style to the Image field.');
$assert_session->linkExists('add an image style to the Image field');
$assert_session->linkByHrefExists('/admin/structure/media/manage/madame_bonacieux/display');
// The image style warning should not include an action link when the
// Field UI module is uninstalled.
$this->container->get('module_installer')->uninstall(['field_ui']);
$this->drupalGet('/admin/reports/status');
$assert_session->pageTextContains('The default display for the Madame Bonacieux media type is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads.');
$assert_session->linkNotExists('add an image style to the Image field');
$assert_session->linkByHrefNotExists('/admin/structure/media/manage/madame_bonacieux/display');
}
/**
* Asserts the proper entity view display components for a media type.
*
* @param string $media_type_id
* The media type ID.
*/
protected function assertViewDisplayConfigured($media_type_id) {
$assert_session = $this->assertSession();
$type = MediaType::load($media_type_id);
$display = EntityViewDisplay::load('media.' . $media_type_id . '.' . EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE);
$this->assertInstanceOf(EntityViewDisplay::class, $display);
$source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
$component = $display->getComponent($source_field_definition->getName());
$this->assertSame('visually_hidden', $component['label']);
if (ImageStyle::load('large')) {
$this->assertSame('large', $component['settings']['image_style']);
}
else {
$this->assertEmpty($component['settings']['image_style']);
}
$this->assertEmpty($component['settings']['image_link']);
// Since components that aren't explicitly hidden can show up on the
// display edit form, check that only the image field appears enabled on
// the display edit form.
$this->drupalGet('/admin/structure/media/manage/' . $media_type_id . '/display');
// Assert that only the source field is enabled.
$assert_session->elementExists('css', 'input[name="' . $source_field_definition->getName() . '_settings_edit"]');
$assert_session->elementsCount('css', 'input[name$="_settings_edit"]', 1);
}
}
@@ -0,0 +1,242 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Session\AccountInterface;
use Drupal\media\Entity\Media;
use Drupal\media_test_oembed\Controller\ResourceController;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
use Drupal\user\Entity\Role;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Tests the oembed:video media source.
*
* @group media
*/
class MediaSourceOEmbedVideoTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['media_test_oembed'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
use OEmbedTestTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->lockHttpClientToFixtures();
}
/**
* {@inheritdoc}
*/
protected function initConfig(ContainerInterface $container) {
parent::initConfig($container);
// Enable twig debugging to make testing template usage easy.
$parameters = $container->getParameter('twig.config');
$parameters['debug'] = TRUE;
$this->setContainerParameter('twig.config', $parameters);
}
/**
* Tests the oembed media source.
*/
public function testMediaOEmbedVideoSource() {
$media_type_id = 'test_media_oembed_type';
$provided_fields = [
'type',
'title',
'default_name',
'author_name',
'author_url',
'provider_name',
'provider_url',
'cache_age',
'thumbnail_uri',
'thumbnail_width',
'thumbnail_height',
'url',
'width',
'height',
'html',
];
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->doTestCreateMediaType($media_type_id, 'oembed:video', $provided_fields);
// Create custom fields for the media type to store metadata attributes.
$fields = [
'field_string_width' => 'string',
'field_string_height' => 'string',
'field_string_author_name' => 'string',
];
$this->createMediaTypeFields($fields, $media_type_id);
// Hide the name field widget to test default name generation.
$this->hideMediaTypeFieldWidget('name', $media_type_id);
$this->drupalGet("admin/structure/media/manage/$media_type_id");
// Only accept Vimeo videos.
$page->checkField("source_configuration[providers][Vimeo]");
$assert_session->selectExists('field_map[width]')->setValue('field_string_width');
$assert_session->selectExists('field_map[height]')->setValue('field_string_height');
$assert_session->selectExists('field_map[author_name]')->setValue('field_string_author_name');
$assert_session->buttonExists('Save')->press();
// Configure the iframe to be narrower than the actual video, so we can
// verify that the video scales correctly.
$display = \Drupal::service('entity_display.repository')->getViewDisplay('media', $media_type_id);
$this->assertFalse($display->isNew());
$component = $display->getComponent('field_media_oembed_video');
$this->assertIsArray($component);
$component['settings']['max_width'] = 240;
$display->setComponent('field_media_oembed_video', $component);
$this->assertSame(SAVED_UPDATED, $display->save());
$this->hijackProviderEndpoints();
$video_url = 'https://vimeo.com/7073899';
ResourceController::setResourceUrl($video_url, $this->getFixturesDirectory() . '/video_vimeo.json');
// Create a media item.
$this->drupalGet("media/add/$media_type_id");
$assert_session->fieldExists('Remote video URL')->setValue($video_url);
$assert_session->buttonExists('Save')->press();
$assert_session->addressEquals('admin/content/media');
// Get the media entity view URL from the creation message.
$this->drupalGet($this->assertLinkToCreatedMedia());
/** @var \Drupal\media\MediaInterface $media */
$media = Media::load(1);
// The thumbnail should have been downloaded.
$thumbnail = $media->getSource()->getMetadata($media, 'thumbnail_uri');
$this->assertFileExists($thumbnail);
// Ensure the iframe exists and has the expected CSS class, and that its src
// attribute contains a coherent URL with the query parameters we expect.
$iframe = $assert_session->elementExists('css', 'iframe.media-oembed-content');
$iframe_url = parse_url($iframe->getAttribute('src'));
$this->assertStringEndsWith('/media/oembed', $iframe_url['path']);
$this->assertNotEmpty($iframe_url['query']);
$query = [];
parse_str($iframe_url['query'], $query);
$this->assertSame($video_url, $query['url']);
$this->assertNotEmpty($query['hash']);
// Ensure that the outer iframe's width respects the formatter settings.
$this->assertSame('240', $iframe->getAttribute('width'));
// Check the inner iframe to make sure that CSS has been applied to scale it
// correctly, regardless of whatever its width attribute may be (the fixture
// hard-codes it to 480).
$inner_frame = 'frames[0].document.querySelector("iframe")';
$this->assertSame('480', $session->evaluateScript("$inner_frame.getAttribute('width')"));
$this->assertLessThanOrEqual(240, $session->evaluateScript("$inner_frame.clientWidth"));
// The oEmbed content iFrame should be visible.
$assert_session->elementExists('css', 'iframe.media-oembed-content');
// The thumbnail should not be displayed.
$assert_session->elementNotExists('css', '.image-style-thumbnail');
// Load the media and check that all fields are properly populated.
$media = Media::load(1);
$this->assertSame('Drupal Rap Video - Schipulcon09', $media->getName());
$this->assertSame('480', $media->field_string_width->value);
$this->assertSame('360', $media->field_string_height->value);
// Try to create a media asset from a disallowed provider.
$this->drupalGet("media/add/$media_type_id");
$assert_session->fieldExists('Remote video URL')->setValue('http://www.collegehumor.com/video/40003213/grant-and-katie-are-starting-their-own-company');
$page->pressButton('Save');
$assert_session->pageTextContains('The CollegeHumor provider is not allowed.');
// Test anonymous access to media via iframe.
$this->drupalLogout();
// Without a hash should be denied.
$no_hash_query = array_diff_key($query, ['hash' => '']);
$this->drupalGet('media/oembed', ['query' => $no_hash_query]);
$assert_session->pageTextNotContains('By the power of Greyskull, Vimeo works!');
$assert_session->pageTextContains('Access denied');
// A correct query should be allowed because the anonymous role has the
// 'view media' permission.
$this->drupalGet('media/oembed', ['query' => $query]);
$assert_session->pageTextContains('By the power of Greyskull, Vimeo works!');
$this->assertRaw('core/themes/stable/templates/content/media-oembed-iframe.html.twig');
$this->assertNoRaw('core/modules/media/templates/media-oembed-iframe.html.twig');
// Test themes not inheriting from stable.
\Drupal::service('theme_installer')->install(['stark']);
$this->config('system.theme')->set('default', 'stark')->save();
$this->drupalGet('media/oembed', ['query' => $query]);
$assert_session->pageTextContains('By the power of Greyskull, Vimeo works!');
$this->assertNoRaw('core/themes/stable/templates/content/media-oembed-iframe.html.twig');
$this->assertRaw('core/modules/media/templates/media-oembed-iframe.html.twig');
// Remove the 'view media' permission to test that this restricts access.
$role = Role::load(AccountInterface::ANONYMOUS_ROLE);
$role->revokePermission('view media');
$role->save();
$this->drupalGet('media/oembed', ['query' => $query]);
$assert_session->pageTextNotContains('By the power of Greyskull, Vimeo works!');
$assert_session->pageTextContains('Access denied');
}
/**
* Test that a security warning appears if iFrame domain is not set.
*/
public function testOEmbedSecurityWarning() {
$media_type_id = 'test_media_oembed_type';
$source_id = 'oembed:video';
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $media_type_id);
$this->getSession()
->wait(5000, "jQuery('.machine-name-value').text() === '{$media_type_id}'");
// Make sure the source is available.
$assert_session->fieldExists('Media source');
$assert_session->optionExists('Media source', $source_id);
$page->selectFieldOption('Media source', $source_id);
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
$assert_session->pageTextContains('It is potentially insecure to display oEmbed content in a frame');
$this->config('media.settings')->set('iframe_domain', 'http://example.com')->save();
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $media_type_id);
$this->getSession()
->wait(5000, "jQuery('.machine-name-value').text() === '{$media_type_id}'");
// Make sure the source is available.
$assert_session->fieldExists('Media source');
$assert_session->optionExists('Media source', $source_id);
$page->selectFieldOption('Media source', $source_id);
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
$assert_session->pageTextNotContains('It is potentially insecure to display oEmbed content in a frame');
}
}
@@ -0,0 +1,175 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\media\Entity\MediaType;
/**
* Base class for media source tests.
*/
abstract class MediaSourceTestBase extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Let's set the canonical flag in the base class of the source tests,
// because every source test has to check the output on the view page.
\Drupal::configFactory()
->getEditable('media.settings')
->set('standalone_url', TRUE)
->save(TRUE);
$this->container->get('router.builder')->rebuild();
}
/**
* Creates storage and field instance, attached to a given media type.
*
* @param string $field_name
* The field name.
* @param string $field_type
* The field type.
* @param string $media_type_id
* The media type config entity ID.
*/
protected function createMediaTypeField($field_name, $field_type, $media_type_id) {
$storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'media',
'type' => $field_type,
]);
$storage->save();
FieldConfig::create([
'field_storage' => $storage,
'bundle' => $media_type_id,
])->save();
// Make the field widget visible in the form display.
$component = \Drupal::service('plugin.manager.field.widget')
->prepareConfiguration($field_type, []);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$entity_form_display = $display_repository->getFormDisplay('media', $media_type_id, 'default');
$entity_form_display->setComponent($field_name, $component)
->save();
// Use the default formatter and settings.
$component = \Drupal::service('plugin.manager.field.formatter')
->prepareConfiguration($field_type, []);
$entity_display = $display_repository->getViewDisplay('media', $media_type_id);
$entity_display->setComponent($field_name, $component)
->save();
}
/**
* Create a set of fields in a media type.
*
* @param array $fields
* An associative array where keys are field names and values field types.
* @param string $media_type_id
* The media type config entity ID.
*/
protected function createMediaTypeFields(array $fields, $media_type_id) {
foreach ($fields as $field_name => $field_type) {
$this->createMediaTypeField($field_name, $field_type, $media_type_id);
}
}
/**
* Hides a widget in the default form display config.
*
* @param string $field_name
* The field name.
* @param string $media_type_id
* The media type config entity ID.
*/
protected function hideMediaTypeFieldWidget($field_name, $media_type_id) {
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity_form_display */
$entity_form_display = $display_repository->getFormDisplay('media', $media_type_id, 'default');
if ($entity_form_display->getComponent($field_name)) {
$entity_form_display->removeComponent($field_name)->save();
}
}
/**
* Test generic media type creation.
*
* @param string $media_type_id
* The media type config entity ID.
* @param string $source_id
* The media source ID.
* @param array $provided_fields
* (optional) An array of field machine names this type provides.
* @param string $source_label_visibility
* (optional) The visibility that the source field label is expected to
* have. Defaults to 'visually_hidden'.
*
* @return \Drupal\media\MediaTypeInterface
* The created media type.
*/
public function doTestCreateMediaType($media_type_id, $source_id, array $provided_fields = [], $source_label_visibility = 'visually_hidden') {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $media_type_id);
$this->getSession()
->wait(5000, "jQuery('.machine-name-value').text() === '{$media_type_id}'");
// Make sure the source is available.
$assert_session->fieldExists('Media source');
$assert_session->optionExists('Media source', $source_id);
$page->selectFieldOption('Media source', $source_id);
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
// Make sure the provided fields are visible on the form.
foreach ($provided_fields as $provided_field) {
$result = $assert_session->waitForElementVisible('css', 'select[name="field_map[' . $provided_field . ']"]');
$this->assertNotEmpty($result);
}
// Save the form to create the type.
$page->pressButton('Save');
$assert_session->pageTextContains('The media type ' . $media_type_id . ' has been added.');
$this->drupalGet('admin/structure/media');
$assert_session->pageTextContains($media_type_id);
$media_type = MediaType::load($media_type_id);
// Assert that the default display of the media type only shows the source
// field.
$this->drupalGet("/admin/structure/media/manage/$media_type_id/display");
// There should be only one field with editable settings, and it should be
// the source field.
$assert_session->elementsCount('css', 'input[name$="_settings_edit"]', 1);
$source_field_name = $media_type->getSource()
->getSourceFieldDefinition($media_type)
->getName();
$assert_session->buttonExists("{$source_field_name}_settings_edit");
// Ensure the source field label is configured as expected.
$assert_session->fieldValueEquals("fields[$source_field_name][label]", $source_label_visibility);
// Bundle definitions are statically cached in the context of the test, we
// need to make sure we have updated information before proceeding with the
// actions on the UI.
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
return $media_type;
}
}
@@ -0,0 +1,553 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\media_test_oembed\Controller\ResourceController;
use Drupal\node\Entity\Node;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
/**
* Basic tests for Media configuration in the standard profile.
*
* @group media
*/
class MediaStandardProfileTest extends MediaJavascriptTestBase {
use OEmbedTestTrait;
/**
* {@inheritdoc}
*/
protected $profile = 'standard';
/**
* {@inheritdoc}
*/
public static $modules = ['media_test_oembed'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->lockHttpClientToFixtures();
$this->hijackProviderEndpoints();
}
/**
* Tests all media sources in one method.
*
* This prevents installing the standard profile for every test case and
* increases the performance of this test.
*/
public function testMediaSources() {
$storage = FieldStorageConfig::create([
'entity_type' => 'node',
'field_name' => 'field_related_media',
'type' => 'entity_reference',
'settings' => [
'target_type' => 'media',
],
]);
$storage->save();
FieldConfig::create([
'field_storage' => $storage,
'entity_type' => 'node',
'bundle' => 'article',
'label' => 'Related media',
'settings' => [
'handler_settings' => [
'target_bundles' => [
'audio' => 'audio',
'document' => 'document',
'image' => 'image',
'remote_video' => 'remote_video',
'video' => 'video',
],
],
],
])->save();
$display = EntityViewDisplay::load('node.article.default');
$display->setComponent('field_related_media', [
'type' => 'entity_reference_entity_view',
'settings' => [
'view_mode' => 'full',
],
])->save();
$this->audioTest();
$this->documentTest();
$this->imageTest();
$this->remoteVideoTest();
$this->videoTest();
}
/**
* Test the standard profile configuration for media type 'audio'.
*/
protected function audioTest() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_field_id = 'field_media_audio_file';
// Create 2 test files.
$test_filename = $this->randomMachineName() . '.mp3';
$test_filepath = 'public://' . $test_filename;
$test_filename_updated = $this->randomMachineName() . '.mp3';
$test_filepath_updated = 'public://' . $test_filename_updated;
file_put_contents($test_filepath, str_repeat('t', 10));
file_put_contents($test_filepath_updated, str_repeat('u', 10));
// Check if the name field is properly hidden on the media form.
$this->drupalGet('media/add/audio');
$assert_session->fieldNotExists('name');
// Check if the source field is available.
$assert_session->fieldExists("files[{$source_field_id}_0]");
// Create a media item.
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$audio_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$audio_media_id = reset($audio_media_id);
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$node = Node::create([
'title' => 'Host node',
'type' => 'article',
'field_related_media' => [
'target_id' => $audio_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is generated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($audio_media_id);
$this->assertSame($test_filename, $media->label());
// Here we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-audio > *', 1);
// Assert the audio file is present inside the media element and that its
// src attribute matches the audio file.
$audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source');
$expected_audio_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename)));
$this->assertSame($expected_audio_src, $audio_element->getAttribute('src'));
// Assert the media name is updated through the field mapping when changing
// the source field.
$this->drupalGet('media/' . $audio_media_id . '/edit');
$page->pressButton('Remove');
$result = $assert_session->waitForField("files[{$source_field_id}_0]");
$this->assertNotEmpty($result);
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath_updated));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is updated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($audio_media_id);
$this->assertSame($test_filename_updated, $media->label());
// Again we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-audio > *', 1);
// Assert the audio file is present inside the media element and that its
// src attribute matches the updated audio file.
$audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source');
$expected_audio_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated)));
$this->assertSame($expected_audio_src, $audio_element->getAttribute('src'));
}
/**
* Test the standard profile configuration for media type 'image'.
*/
protected function imageTest() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_field_id = 'field_media_image';
// Check if the name field is properly hidden on the media form.
$this->drupalGet('media/add/image');
$assert_session->fieldNotExists('name');
// Check if the source field is available.
$assert_session->fieldExists("files[{$source_field_id}_0]");
// Create a media item.
$image_media_name = 'example_1.jpeg';
$page->attachFileToField("files[{$source_field_id}_0]", $this->root . '/core/modules/media/tests/fixtures/' . $image_media_name);
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->fillField("{$source_field_id}[0][alt]", 'Image Alt Text 1');
$page->pressButton('Save');
$image_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$image_media_id = reset($image_media_id);
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$node = Node::create([
'title' => 'Host node',
'type' => 'article',
'field_related_media' => [
'target_id' => $image_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is generated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($image_media_id);
$this->assertSame($image_media_name, $media->label());
// Here we expect to see only the image, nothing else. Assert only one
// element in the content region.
$assert_session->elementsCount('css', 'article.media--type-image > *', 1);
// Assert the image element is present inside the media element and that its
// src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file.
$image_element = $assert_session->elementExists('css', 'article.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name)));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden');
$assert_session->elementNotExists('css', '.field--name-field-media-image a');
// Assert the media name is updated through the field mapping when changing
// the source field.
$this->drupalGet('media/' . $image_media_id . '/edit');
$page->pressButton('Remove');
$result = $assert_session->waitForField("files[{$source_field_id}_0]");
$this->assertNotEmpty($result);
$image_media_name_updated = 'example_2.jpeg';
$page->attachFileToField("files[{$source_field_id}_0]", $this->root . '/core/modules/media/tests/fixtures/' . $image_media_name_updated);
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->fillField("{$source_field_id}[0][alt]", 'Image Alt Text 2');
$page->pressButton('Save');
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is updated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($image_media_id);
$this->assertSame($image_media_name_updated, $media->label());
// Again we expect to see only the image, nothing else. Assert only one
// element in the content region.
$assert_session->elementsCount('css', 'article.media--type-image > *', 1);
// Assert the image element is present inside the media element and that its
// src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file.
$image_element = $assert_session->elementExists('css', 'article.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name_updated)));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden');
$assert_session->elementNotExists('css', '.field--name-field-media-image a');
}
/**
* Test the standard profile configuration for media type 'document'.
*/
protected function documentTest() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_field_id = 'field_media_document';
// Create 2 test files.
$test_filename = $this->randomMachineName() . '.txt';
$test_filepath = 'public://' . $test_filename;
$test_filename_updated = $this->randomMachineName() . '.txt';
$test_filepath_updated = 'public://' . $test_filename_updated;
file_put_contents($test_filepath, $this->randomMachineName());
file_put_contents($test_filepath_updated, $this->randomMachineName());
// Check if the name field is properly hidden on the media form.
$this->drupalGet('media/add/document');
$assert_session->fieldNotExists('name');
// Check if the source field is available.
$assert_session->fieldExists("files[{$source_field_id}_0]");
// Create a media item.
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$file_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$file_media_id = reset($file_media_id);
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$node = Node::create([
'title' => 'Host node',
'type' => 'article',
'field_related_media' => [
'target_id' => $file_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is generated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($file_media_id);
$this->assertSame($test_filename, $media->label());
// Here we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-document > *', 1);
// Assert the file link is present in the media element and its text matches
// the filename.
$link_element = $assert_session->elementExists('css', 'article.media--type-document .field--name-field-media-document a');
$this->assertSame($test_filename, $link_element->getText());
// Assert the media name is updated through the field mapping when changing
// the source field.
$this->drupalGet('media/' . $file_media_id . '/edit');
$page->pressButton('Remove');
$result = $assert_session->waitForField("files[{$source_field_id}_0]");
$this->assertNotEmpty($result);
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath_updated));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is updated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($file_media_id);
$this->assertSame($test_filename_updated, $media->label());
// Again we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-document > *', 1);
// Assert the file link is present in the media element and its text matches
// the updated filename.
$link_element = $assert_session->elementExists('css', 'article.media--type-document .field--name-field-media-document a');
$this->assertSame($test_filename_updated, $link_element->getText());
}
/**
* Test the standard profile configuration for media type 'remote_video'.
*/
protected function remoteVideoTest() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_field_id = 'field_media_oembed_video';
// Set video fixtures.
$video_title = 'Drupal Rap Video - Schipulcon09';
$video_url = 'https://vimeo.com/7073899';
ResourceController::setResourceUrl($video_url, $this->getFixturesDirectory() . '/video_vimeo.json');
$video_title_updated = "Everyday I'm Drupalin' Drupal Rap (Rick Ross - Hustlin)";
$video_url_updated = 'https://www.youtube.com/watch?v=PWjcqE3QKBg';
ResourceController::setResourceUrl($video_url_updated, $this->getFixturesDirectory() . '/video_youtube.json');
// Check if the name field is properly hidden on the media form.
$this->drupalGet('media/add/remote_video');
$assert_session->fieldNotExists('name');
// Check if the source field is available.
$assert_session->fieldExists("{$source_field_id}[0][value]");
// Create a media item.
$page->fillField("{$source_field_id}[0][value]", $video_url);
$page->pressButton('Save');
$remote_video_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$remote_video_media_id = reset($remote_video_media_id);
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$node = Node::create([
'title' => 'Host node',
'type' => 'article',
'field_related_media' => [
'target_id' => $remote_video_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is generated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($remote_video_media_id);
$this->assertSame($video_title, $media->label());
// Here we expect to see only the video iframe. Assert only one element in
// the content region.
$assert_session->elementsCount('css', 'article.media--type-remote-video > *', 1);
// Assert the iframe is present in the media element and its src attribute
// matches the URL and query parameters.
$iframe_url = $assert_session->elementExists('css', 'article.media--type-remote-video .field--name-field-media-oembed-video iframe')->getAttribute('src');
$iframe_url = parse_url($iframe_url);
$this->assertStringEndsWith('/media/oembed', $iframe_url['path']);
$this->assertNotEmpty($iframe_url['query']);
$query = [];
parse_str($iframe_url['query'], $query);
$this->assertSame($video_url, $query['url']);
$this->assertNotEmpty($query['hash']);
// Assert the media name is updated through the field mapping when changing
// the source field.
$this->drupalGet('media/' . $remote_video_media_id . '/edit');
$page->fillField("{$source_field_id}[0][value]", $video_url_updated);
$page->pressButton('Save');
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is updated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($remote_video_media_id);
$this->assertSame($video_title_updated, $media->label());
// Again we expect to see only the video iframe. Assert only one element in
// the content region.
$assert_session->elementsCount('css', 'article.media--type-remote-video > *', 1);
// Assert the iframe is present in the media element and its src attribute
// matches the updated URL and query parameters.
$iframe_url = $assert_session->elementExists('css', 'article.media--type-remote-video .field--name-field-media-oembed-video iframe')->getAttribute('src');
$iframe_url = parse_url($iframe_url);
$this->assertStringEndsWith('/media/oembed', $iframe_url['path']);
$this->assertNotEmpty($iframe_url['query']);
$query = [];
parse_str($iframe_url['query'], $query);
$this->assertSame($video_url_updated, $query['url']);
$this->assertNotEmpty($query['hash']);
}
/**
* Test the standard profile configuration for media type 'video'.
*/
protected function videoTest() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_field_id = 'field_media_video_file';
// Create 2 test files.
$test_filename = $this->randomMachineName() . '.mp4';
$test_filepath = 'public://' . $test_filename;
$test_filename_updated = $this->randomMachineName() . '.mp4';
$test_filepath_updated = 'public://' . $test_filename_updated;
file_put_contents($test_filepath, str_repeat('t', 10));
file_put_contents($test_filepath_updated, str_repeat('u', 10));
// Check if the name field is properly hidden on the media form.
$this->drupalGet('media/add/video');
$assert_session->fieldNotExists('name');
// Check if the source field is available.
$assert_session->fieldExists("files[{$source_field_id}_0]");
// Create a media item.
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$video_media_id = $this->container
->get('entity_type.manager')
->getStorage('media')
->getQuery()
->sort('mid', 'DESC')
->execute();
$video_media_id = reset($video_media_id);
// Reference the created media using an entity_reference field and make sure
// the output is what we expect.
$node = Node::create([
'title' => 'Host node',
'type' => 'article',
'field_related_media' => [
'target_id' => $video_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is generated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($video_media_id);
$this->assertSame($test_filename, $media->label());
// Here we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-video > *', 1);
// Assert the video element is present inside the media element and that its
// src attribute matches the video file.
$video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source');
$expected_video_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename)));
$this->assertSame($expected_video_src, $video_element->getAttribute('src'));
// Assert the media name is updated through the field mapping when changing
// the source field.
$this->drupalGet('media/' . $video_media_id . '/edit');
$page->pressButton('Remove');
$result = $assert_session->waitForField("files[{$source_field_id}_0]");
$this->assertNotEmpty($result);
$page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath_updated));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$this->drupalGet('/node/' . $node->id());
// Check if the default media name is updated as expected.
$media = \Drupal::entityTypeManager()->getStorage('media')->loadUnchanged($video_media_id);
$this->assertSame($test_filename_updated, $media->label());
// Again we expect to see only the linked filename. Assert only one element
// in the content region.
$assert_session->elementsCount('css', 'article.media--type-video > *', 1);
// Assert the video element is present inside the media element and that its
// src attribute matches the updated video file.
$video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source');
$expected_video_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated)));
$this->assertSame($expected_video_src, $video_element->getAttribute('src'));
}
}
@@ -0,0 +1,186 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Component\Utility\Html;
/**
* Tests the media type creation.
*
* @group media
*/
class MediaTypeCreationTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the source field behavior on the add media type form.
*/
public function testSourceChangeOnMediaTypeCreationForm() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$label = 'Type with Default Field';
$mediaTypeMachineName = str_replace(' ', '_', strtolower($label));
$this->drupalGet('admin/structure/media/add');
// Fill in a label to the media type.
$page->fillField('label', $label);
$this->assertNotEmpty(
$assert_session->waitForElementVisible('css', '.machine-name-value')
);
// Select the media source used by our media type.
$assert_session->selectExists('Media source')->selectOption('test_different_displays');
$this->assertNotEmpty(
$assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]')
);
// Change the media source.
$assert_session->selectExists('Media source')->selectOption('test');
$this->assertNotEmpty(
$assert_session->waitForElement('css', 'fieldset[data-drupal-selector="edit-source-configuration"] .fieldset-wrapper .placeholder:contains("Text (plain)")')
);
$page->pressButton('Save');
// Check that source can not be changed anymore.
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}");
$assert_session->pageTextContains('The media source cannot be changed after the media type is created');
$assert_session->fieldDisabled('Media source');
}
/**
* Tests the media type creation form.
*/
public function testMediaTypeCreationFormWithDefaultField() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$label = 'Type with Default Field';
$mediaTypeMachineName = str_replace(' ', '_', strtolower($label));
$this->drupalGet('admin/structure/media/add');
// Select the media source used by our media type. Do this before setting
// the label or machine name in order to guard against the regression in
// https://www.drupal.org/project/drupal/issues/2557299.
$assert_session->fieldExists('Media source');
$assert_session->optionExists('Media source', 'test');
$page->selectFieldOption('Media source', 'test');
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
// Fill in a label to the media type.
$page->fillField('label', $label);
// Wait for machine name generation. Default: waitUntilVisible(), does not
// work properly.
$session->wait(5000, "jQuery('.machine-name-value').text() === '{$mediaTypeMachineName}'");
$page->pressButton('Save');
// Check whether the source field was correctly created.
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}/fields");
// Check 2nd column of first data row, to be machine name for field name.
$assert_session->elementContains('xpath', '(//table[@id="field-overview"]//tr)[2]//td[2]', 'field_media_test');
// Check 3rd column of first data row, to be correct field type.
$assert_session->elementTextContains('xpath', '(//table[@id="field-overview"]//tr)[2]//td[3]', 'Text (plain)');
// Check that the source field is correctly assigned to media type.
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}");
$assert_session->pageTextContains('Test source field is used to store the essential information about the media item.');
// Check that the plugin cannot be changed after it is set on type creation.
$assert_session->fieldDisabled('Media source');
$assert_session->pageTextContains('The media source cannot be changed after the media type is created.');
// Open up the media add form and verify that the source field is right
// after the name, and before the vertical tabs.
$this->drupalGet("/media/add/$mediaTypeMachineName");
// Get the form element, and its HTML representation.
$form_selector = '#media-' . Html::cleanCssIdentifier($mediaTypeMachineName) . '-add-form';
$form = $assert_session->elementExists('css', $form_selector);
$form_html = $form->getOuterHtml();
// The name field should come before the source field, which should itself
// come before the vertical tabs.
$name_field = $assert_session->fieldExists('Name', $form)->getOuterHtml();
$test_source_field = $assert_session->fieldExists('Test source', $form)->getOuterHtml();
$vertical_tabs = $assert_session->elementExists('css', '.vertical-tabs', $form)->getOuterHtml();
$date_field = $assert_session->fieldExists('Date', $form)->getOuterHtml();
$published_checkbox = $assert_session->fieldExists('Published', $form)->getOuterHtml();
$this->assertTrue(strpos($form_html, $test_source_field) > strpos($form_html, $name_field));
$this->assertTrue(strpos($form_html, $vertical_tabs) > strpos($form_html, $test_source_field));
// The "Published" checkbox should be the last element.
$this->assertTrue(strpos($form_html, $published_checkbox) > strpos($form_html, $date_field));
// Check that a new type with the same machine name cannot be created.
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $label);
$session->wait(5000, "jQuery('.machine-name-value').text() === '{$mediaTypeMachineName}'");
$page->selectFieldOption('Media source', 'test');
$assert_session->assertWaitOnAjaxRequest();
$page->pressButton('Save');
$assert_session->pageTextContains('The machine-readable name is already in use. It must be unique.');
}
/**
* Test creation of media type, reusing an existing source field.
*/
public function testMediaTypeCreationReuseSourceField() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
// Create a new media type, which should create a new field we can reuse.
$this->drupalGet('/admin/structure/media/add');
// Choose the source plugin before setting the label and machine name.
$page->selectFieldOption('Media source', 'test');
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
$page->fillField('label', 'Pastafazoul');
$session->wait(5000, "jQuery('.machine-name-value').text() === 'pastafazoul'");
$page->pressButton('Save');
$label = 'Type reusing Default Field';
$mediaTypeMachineName = str_replace(' ', '_', strtolower($label));
$this->drupalGet('admin/structure/media/add');
// Select the media source used by our media type. Do this before setting
// the label and machine name.
$assert_session->fieldExists('Media source');
$assert_session->optionExists('Media source', 'test');
$page->selectFieldOption('Media source', 'test');
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
// Select the existing field for re-use.
$page->selectFieldOption('source_configuration[source_field]', 'field_media_test');
// Fill in a label to the media type.
$page->fillField('label', $label);
// Wait for machine name generation. Default: waitUntilVisible(), does not
// work properly.
$session->wait(5000, "jQuery('.machine-name-value').text() === '{$mediaTypeMachineName}'");
$page->pressButton('Save');
// Check that no new fields were created.
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}/fields");
// The reused field should be present...
$assert_session->pageTextContains('field_media_test');
// ...not a new, unique one.
$assert_session->pageTextNotContains('field_media_test_1');
}
}
@@ -0,0 +1,210 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\field\FieldConfigInterface;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media\MediaSourceInterface;
/**
* Ensures that media UI works correctly.
*
* @group media
*/
class MediaUiJavascriptTest extends MediaJavascriptTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'block',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The test media type.
*
* @var \Drupal\media\MediaTypeInterface
*/
protected $testMediaType;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests a media type administration.
*/
public function testMediaTypes() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->drupalGet('admin/structure/media');
$assert_session->pageTextContains('No media types available. Add media type.');
$assert_session->linkExists('Add media type');
// Test the creation of a media type using the UI.
$name = $this->randomMachineName();
$description = $this->randomMachineName();
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $name);
$machine_name = strtolower($name);
$this->assertJsCondition("jQuery('.machine-name-value').html() == '$machine_name'");
$page->selectFieldOption('source', 'test');
$this->assertJsCondition("jQuery('.form-item-source-configuration-test-config-value').length > 0;");
$page->fillField('description', $description);
$page->pressButton('Save');
// The wait prevents intermittent test failures.
$result = $assert_session->waitForLink('Add media type');
$this->assertNotEmpty($result);
$assert_session->addressEquals('admin/structure/media');
$assert_session->pageTextContains('The media type ' . $name . ' has been added.');
$this->drupalGet('admin/structure/media');
$assert_session->pageTextContains($name);
$assert_session->pageTextContains($description);
// We need to clear the statically cached field definitions to account for
// fields that have been created by API calls in this test, since they exist
// in a separate memory space from the web server.
$this->container->get('entity_field.manager')->clearCachedFieldDefinitions();
// Assert that the field and field storage were created.
$media_type = MediaType::load($machine_name);
$source = $media_type->getSource();
/** @var \Drupal\field\FieldConfigInterface $source_field */
$source_field = $source->getSourceFieldDefinition($media_type);
$this->assertInstanceOf(FieldConfigInterface::class, $source_field);
$this->assertFalse($source_field->isNew(), 'Source field was saved.');
/** @var \Drupal\field\FieldStorageConfigInterface $storage */
$storage = $source_field->getFieldStorageDefinition();
$this->assertFalse($storage->isNew(), 'Source field storage definition was saved.');
$this->assertFalse($storage->isLocked(), 'Source field storage definition was not locked.');
/** @var \Drupal\media\MediaTypeInterface $media_type_storage */
$media_type_storage = $this->container->get('entity_type.manager')->getStorage('media_type');
$this->testMediaType = $media_type_storage->load(strtolower($name));
// Check if all action links exist.
$assert_session->linkByHrefExists('admin/structure/media/add');
$assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testMediaType->id());
$assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testMediaType->id() . '/fields');
$assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testMediaType->id() . '/form-display');
$assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testMediaType->id() . '/display');
// Assert that fields have expected values before editing.
$page->clickLink('Edit');
$assert_session->fieldValueEquals('label', $name);
$assert_session->fieldValueEquals('description', $description);
$assert_session->fieldValueEquals('source', 'test');
$assert_session->fieldValueEquals('label', $name);
$assert_session->checkboxNotChecked('edit-options-new-revision');
$assert_session->checkboxChecked('edit-options-status');
$assert_session->checkboxNotChecked('edit-options-queue-thumbnail-downloads');
$assert_session->pageTextContains('Create new revision');
$assert_session->pageTextContains('Automatically create new revisions. Users with the "Administer media" permission will be able to override this option.');
$assert_session->pageTextContains('Download thumbnails via a queue.');
$assert_session->pageTextContains('Media will be automatically published when created.');
$assert_session->pageTextContains('Media sources can provide metadata fields such as title, caption, size information, credits, etc. Media can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.');
// Try to change media type and check if new configuration sub-form appears.
$page->selectFieldOption('source', 'test');
$result = $assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]');
$this->assertNotEmpty($result);
$assert_session->fieldExists('Test config value');
$assert_session->fieldValueEquals('Test config value', 'This is default value.');
$assert_session->fieldExists('Attribute 1');
$assert_session->fieldExists('Attribute 2');
// Test if the edit machine name is not editable.
$assert_session->fieldDisabled('Machine-readable name');
// Edit and save media type form fields with new values.
$new_name = $this->randomMachineName();
$new_description = $this->randomMachineName();
$page->fillField('label', $new_name);
$page->fillField('description', $new_description);
$page->selectFieldOption('source', 'test');
$page->fillField('Test config value', 'This is new config value.');
$page->selectFieldOption('field_map[attribute_1]', 'name');
$page->checkField('options[new_revision]');
$page->uncheckField('options[status]');
$page->checkField('options[queue_thumbnail_downloads]');
$page->pressButton('Save');
// The wait prevents intermittent test failures.
$result = $assert_session->waitForLink('Add media type');
$this->assertNotEmpty($result);
$assert_session->addressEquals('admin/structure/media');
$assert_session->pageTextContains("The media type $new_name has been updated.");
// Test if edit worked and if new field values have been saved as expected.
$this->drupalGet('admin/structure/media/manage/' . $this->testMediaType->id());
$assert_session->fieldValueEquals('label', $new_name);
$assert_session->fieldValueEquals('description', $new_description);
$assert_session->fieldValueEquals('source', 'test');
$assert_session->checkboxChecked('options[new_revision]');
$assert_session->checkboxNotChecked('options[status]');
$assert_session->checkboxChecked('options[queue_thumbnail_downloads]');
$assert_session->fieldValueEquals('Test config value', 'This is new config value.');
$assert_session->fieldValueEquals('Attribute 1', 'name');
$assert_session->fieldValueEquals('Attribute 2', MediaSourceInterface::METADATA_FIELD_EMPTY);
/** @var \Drupal\media\MediaTypeInterface $loaded_media_type */
$loaded_media_type = $this->container->get('entity_type.manager')
->getStorage('media_type')
->load($this->testMediaType->id());
$this->assertSame($loaded_media_type->id(), $this->testMediaType->id());
$this->assertSame($loaded_media_type->label(), $new_name);
$this->assertSame($loaded_media_type->getDescription(), $new_description);
$this->assertSame($loaded_media_type->getSource()->getPluginId(), 'test');
$this->assertSame($loaded_media_type->getSource()->getConfiguration()['test_config_value'], 'This is new config value.');
$this->assertTrue($loaded_media_type->shouldCreateNewRevision());
$this->assertTrue($loaded_media_type->thumbnailDownloadsAreQueued());
$this->assertFalse($loaded_media_type->getStatus());
$this->assertSame($loaded_media_type->getFieldMap(), ['attribute_1' => 'name']);
// We need to clear the statically cached field definitions to account for
// fields that have been created by API calls in this test, since they exist
// in a separate memory space from the web server.
$this->container->get('entity_field.manager')->clearCachedFieldDefinitions();
// Test that a media item being created with default status to "FALSE",
// will be created unpublished.
/** @var \Drupal\media\MediaInterface $unpublished_media */
$unpublished_media = Media::create(['name' => 'unpublished test media', 'bundle' => $loaded_media_type->id()]);
$this->assertFalse($unpublished_media->isPublished());
$unpublished_media->delete();
// Tests media type delete form.
$page->clickLink('Delete');
$assert_session->addressEquals('admin/structure/media/manage/' . $this->testMediaType->id() . '/delete');
$page->pressButton('Delete');
$assert_session->addressEquals('admin/structure/media');
$assert_session->pageTextContains('The media type ' . $new_name . ' has been deleted.');
// Test that the system for preventing the deletion of media types works
// (they cannot be deleted if there is media content of that type/bundle).
$media_type2 = $this->createMediaType('test');
$label2 = $media_type2->label();
$media = Media::create(['name' => 'lorem ipsum', 'bundle' => $media_type2->id()]);
$media->save();
$this->drupalGet('admin/structure/media/manage/' . $media_type2->id());
$page->clickLink('Delete');
$assert_session->addressEquals('admin/structure/media/manage/' . $media_type2->id() . '/delete');
$assert_session->buttonNotExists('edit-submit');
$assert_session->pageTextContains("$label2 is used by 1 media item on your site. You can not remove this media type until you have removed all of the $label2 media items.");
}
}
@@ -0,0 +1,94 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\views\Views;
/**
* Tests the media entity type integration into the wizard.
*
* @group media
*
* @see \Drupal\media\Plugin\views\wizard\Media
* @see \Drupal\media\Plugin\views\wizard\MediaRevision
*/
class MediaViewsWizardTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests adding a view of media.
*/
public function testMediaWizard() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->createMediaType('test');
$view_id = strtolower($this->randomMachineName(16));
$this->drupalGet('admin/structure/views/add');
$page->fillField('label', $view_id);
$this->waitUntilVisible('.machine-name-value');
$page->selectFieldOption('show[wizard_key]', 'media');
$result = $assert_session->waitForElementVisible('css', 'select[data-drupal-selector="edit-show-type"]');
$this->assertNotEmpty($result);
$page->checkField('page[create]');
$page->fillField('page[path]', $this->randomMachineName(16));
$page->pressButton('Save and edit');
$this->assertSame($session->getCurrentUrl(), $this->baseUrl . '/admin/structure/views/view/' . $view_id);
$view = Views::getView($view_id);
$view->initHandlers();
$row = $view->display_handler->getOption('row');
$this->assertSame($row['type'], 'fields');
// Check for the default filters.
$this->assertSame($view->filter['status']->table, 'media_field_data');
$this->assertSame($view->filter['status']->field, 'status');
$this->assertSame($view->filter['status']->value, '1');
// Check for the default fields.
$this->assertSame($view->field['name']->table, 'media_field_data');
$this->assertSame($view->field['name']->field, 'name');
}
/**
* Tests adding a view of media revisions.
*/
public function testMediaRevisionWizard() {
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$view_id = strtolower($this->randomMachineName(16));
$this->drupalGet('admin/structure/views/add');
$page->fillField('label', $view_id);
$this->waitUntilVisible('.machine-name-value');
$page->selectFieldOption('show[wizard_key]', 'media_revision');
$assert_session->assertWaitOnAjaxRequest();
$page->checkField('page[create]');
$page->fillField('page[path]', $this->randomMachineName(16));
$page->pressButton('Save and edit');
$this->assertSame($session->getCurrentUrl(), $this->baseUrl . '/admin/structure/views/view/' . $view_id);
$view = Views::getView($view_id);
$view->initHandlers();
$row = $view->display_handler->getOption('row');
$this->assertSame($row['type'], 'fields');
// Check for the default filters.
$this->assertSame($view->filter['status']->table, 'media_field_revision');
$this->assertSame($view->filter['status']->field, 'status');
$this->assertSame($view->filter['status']->value, '1');
// Check for the default fields.
$this->assertSame($view->field['name']->table, 'media_field_revision');
$this->assertSame($view->field['name']->field, 'name');
$this->assertSame($view->field['changed']->table, 'media_field_revision');
$this->assertSame($view->field['changed']->field, 'changed');
}
}
@@ -0,0 +1,92 @@
<?php
namespace Drupal\Tests\media\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\editor\EditorInterface;
use Drupal\filter\Entity\FilterFormat;
use Drupal\KernelTests\KernelTestBase;
use Drupal\media\Entity\Media;
use Drupal\media\Form\EditorMediaDialog;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
/**
* @coversDefaultClass \Drupal\media\Form\EditorMediaDialog
* @group media
*/
class EditorMediaDialogTest extends KernelTestBase {
use MediaTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'field',
'file',
'filter',
'image',
'media',
'media_test_source',
'system',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('file', ['file_usage']);
$this->installEntitySchema('file');
$this->installEntitySchema('media');
$this->installEntitySchema('user');
}
/**
* Tests that the form builds successfully.
*
* @covers ::buildForm
*/
public function testBuildForm() {
$format = FilterFormat::create([
'filters' => [
'media_embed' => ['status' => TRUE],
],
]);
$editor = $this->prophesize(EditorInterface::class);
$editor->getFilterFormat()->willReturn($format);
// Create a sample media entity to be embedded.
$media = Media::create([
'bundle' => $this->createMediaType('test')->id(),
'name' => 'Screaming hairy armadillo',
'field_media_test' => $this->randomString(),
]);
$media->save();
$form_state = new FormState();
$form_state->setUserInput([
'editor_object' => [
'attributes' => [
'data-entity-type' => 'media',
'data-entity-uuid' => $media->uuid(),
'data-align' => 'center',
],
'hasCaption' => 'false',
'label' => $media->label(),
'link' => '',
'hostEntityLangcode' => $media->language()->getId(),
'classes' => '',
],
]);
$form_state->setRequestMethod('POST');
EditorMediaDialog::create($this->container)
->buildForm([], $form_state, $editor->reveal());
$this->pass('Form was built without errors.');
}
}

Some files were not shown because too many files have changed in this diff Show More