mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW Use embed/embed v4
This commit is contained in:
parent
081ec34461
commit
9add508718
@ -3,4 +3,7 @@ Name: coreoembed
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\View\Embed\Embeddable:
|
||||
class: SilverStripe\View\Embed\EmbedResource
|
||||
class: SilverStripe\View\Embed\EmbedContainer
|
||||
Embed\Embed:
|
||||
constructor:
|
||||
- '%$Embed\Http\Crawler'
|
||||
|
@ -25,8 +25,8 @@
|
||||
"bramus/monolog-colored-line-formatter": "^2",
|
||||
"composer/installers": "^1 || ^2",
|
||||
"composer/semver": "^1 || ^3",
|
||||
"embed/embed": "^3",
|
||||
"guzzlehttp/psr7": "^2",
|
||||
"embed/embed": "^4",
|
||||
"league/csv": "^8 || ^9",
|
||||
"m1/env": "^2.1",
|
||||
"monolog/monolog": "^1.16",
|
||||
|
138
src/View/Embed/EmbedContainer.php
Normal file
138
src/View/Embed/EmbedContainer.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Embed;
|
||||
|
||||
use Embed\Extractor;
|
||||
use Embed\Embed;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||
|
||||
/**
|
||||
* This class acts as a wrapper around the third party requirement embed/embed v4
|
||||
*/
|
||||
class EmbedContainer implements Embeddable
|
||||
{
|
||||
use Injectable;
|
||||
|
||||
private static $dependencies = [
|
||||
'embed' => '%$' . Embed::class,
|
||||
];
|
||||
|
||||
public Embed $embed;
|
||||
|
||||
private ?Extractor $extractor = null;
|
||||
|
||||
private string $url;
|
||||
|
||||
private array $options = [];
|
||||
|
||||
public function __construct(string $url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
$code = $this->getExtractor()->code;
|
||||
return $code ? ($code->width ?: 100) : 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
$code = $this->getExtractor()->code;
|
||||
return $code ? ($code->height ?: 100) : 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviewURL()
|
||||
{
|
||||
$extractor = $this->getExtractor();
|
||||
|
||||
// Use thumbnail url
|
||||
if ($extractor->image) {
|
||||
return (string) $extractor->image;
|
||||
}
|
||||
|
||||
// Default media
|
||||
return ModuleResourceLoader::resourceURL(
|
||||
'silverstripe/asset-admin:client/dist/images/icon_file.png'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
$extractor = $this->getExtractor();
|
||||
if ($extractor->title) {
|
||||
return $extractor->title;
|
||||
}
|
||||
if ($extractor->url instanceof UriInterface) {
|
||||
return basename($extractor->url->getPath());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
$html = $this->getExtractor()->code->html ?? '';
|
||||
if (strpos($html, '<video') !== false) {
|
||||
return 'video';
|
||||
}
|
||||
if (strpos($html, '<audio') !== false) {
|
||||
return 'audio';
|
||||
}
|
||||
foreach (['iframe', 'blockquote', 'pre', 'script', 'style'] as $richTag) {
|
||||
if (strpos($html, "<{$richTag}") !== false) {
|
||||
return 'rich';
|
||||
}
|
||||
}
|
||||
if (strpos($html, '<img') !== false) {
|
||||
return 'photo';
|
||||
}
|
||||
return 'link';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
return !empty($this->getExtractor()->code->html ?? '');
|
||||
}
|
||||
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function setOptions(array $options): self
|
||||
{
|
||||
$this->options = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this method will trigger the HTTP call(s) to the remote url
|
||||
*/
|
||||
public function getExtractor(): Extractor
|
||||
{
|
||||
if (!$this->extractor) {
|
||||
$this->extractor = $this->embed->get($this->url);
|
||||
}
|
||||
return $this->extractor;
|
||||
}
|
||||
}
|
@ -6,11 +6,16 @@ use Embed\Adapters\Adapter;
|
||||
use Embed\Embed;
|
||||
use Embed\Http\DispatcherInterface;
|
||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
|
||||
/**
|
||||
* This is a deprecated class that was compatible with embed/embed v3
|
||||
* This has been replaced with EmbedContainer which is embed/embed v4 compatible
|
||||
*
|
||||
* Encapsulation of an embed tag, linking to an external media source.
|
||||
*
|
||||
* @see Embed
|
||||
* @deprecated 4.11..5.0 Use EmbedContainer instead
|
||||
*/
|
||||
class EmbedResource implements Embeddable
|
||||
{
|
||||
@ -41,6 +46,7 @@ class EmbedResource implements Embeddable
|
||||
*/
|
||||
public function __construct($url)
|
||||
{
|
||||
Deprecation::notice('4.11', 'Use EmbedContainer instead');
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ namespace SilverStripe\View\Embed;
|
||||
/**
|
||||
* Abstract interface for an embeddable resource
|
||||
*
|
||||
* @see EmbedResource
|
||||
* @see EmbedContainer
|
||||
*/
|
||||
interface Embeddable
|
||||
{
|
||||
|
@ -2,23 +2,22 @@
|
||||
|
||||
namespace SilverStripe\View\Shortcodes;
|
||||
|
||||
use Embed\Http\DispatcherInterface;
|
||||
use Embed\Http\NetworkException;
|
||||
use Embed\Http\RequestException;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Extensible;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Embed\Embeddable;
|
||||
use SilverStripe\View\Embed\EmbedResource;
|
||||
use SilverStripe\View\HTML;
|
||||
use SilverStripe\View\Parsers\ShortcodeHandler;
|
||||
use Embed\Adapters\Adapter;
|
||||
use Embed\Exceptions\InvalidUrlException;
|
||||
use SilverStripe\View\Parsers\ShortcodeParser;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\View\Embed\EmbedContainer;
|
||||
|
||||
/**
|
||||
* Provider for the [embed] shortcode tag used by the embedding service
|
||||
@ -84,32 +83,23 @@ class EmbedShortcodeProvider implements ShortcodeHandler
|
||||
$serviceArguments['min_image_height'] = $arguments['height'];
|
||||
}
|
||||
|
||||
/** @var EmbedResource $embed */
|
||||
$embed = Injector::inst()->create(Embeddable::class, $serviceURL);
|
||||
/** @var EmbedContainer $embeddable */
|
||||
$embeddable = Injector::inst()->create(Embeddable::class, $serviceURL);
|
||||
|
||||
// Only EmbedContainer is currently supported
|
||||
if (!($embeddable instanceof EmbedContainer)) {
|
||||
throw new \RuntimeException('Emeddable must extend EmbedContainer');
|
||||
}
|
||||
|
||||
if (!empty($serviceArguments)) {
|
||||
$embed->setOptions(array_merge($serviceArguments, (array) $embed->getOptions()));
|
||||
}
|
||||
|
||||
// Allow resolver to be mocked
|
||||
$dispatcher = null;
|
||||
if (isset($extra['resolver'])) {
|
||||
$dispatcher = Injector::inst()->create(
|
||||
$extra['resolver']['class'],
|
||||
$serviceURL,
|
||||
$extra['resolver']['config']
|
||||
);
|
||||
} elseif (Injector::inst()->has(DispatcherInterface::class)) {
|
||||
$dispatcher = Injector::inst()->get(DispatcherInterface::class);
|
||||
}
|
||||
|
||||
if ($dispatcher) {
|
||||
$embed->setDispatcher($dispatcher);
|
||||
$embeddable->setOptions(array_merge($serviceArguments, (array) $embeddable->getOptions()));
|
||||
}
|
||||
|
||||
// Process embed
|
||||
try {
|
||||
$embed = $embed->getEmbed();
|
||||
} catch (InvalidUrlException $e) {
|
||||
// this will trigger a request/response which will then be cached within $embeddable
|
||||
$embeddable->getExtractor();
|
||||
} catch (NetworkException | RequestException $e) {
|
||||
$message = (Director::isDev())
|
||||
? $e->getMessage()
|
||||
: _t(__CLASS__ . '.INVALID_URL', 'There was a problem loading the media.');
|
||||
@ -127,30 +117,54 @@ class EmbedShortcodeProvider implements ShortcodeHandler
|
||||
}
|
||||
|
||||
// Convert embed object into HTML
|
||||
if ($embed && $embed instanceof Adapter) {
|
||||
$result = static::embedForTemplate($embed, $arguments);
|
||||
}
|
||||
$html = static::embeddableToHtml($embeddable, $arguments);
|
||||
// Fallback to link to service
|
||||
if (!$result) {
|
||||
if (!$html) {
|
||||
$result = static::linkEmbed($arguments, $serviceURL, $serviceURL);
|
||||
}
|
||||
// Cache result
|
||||
if ($result) {
|
||||
if ($html) {
|
||||
try {
|
||||
$cache->set($key, $result);
|
||||
$cache->set($key, $html);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
return $html;
|
||||
}
|
||||
|
||||
public static function embeddableToHtml(Embeddable $embeddable, array $arguments): string
|
||||
{
|
||||
// Only EmbedContainer is supported
|
||||
if (!($embeddable instanceof EmbedContainer)) {
|
||||
return '';
|
||||
}
|
||||
$extractor = $embeddable->getExtractor();
|
||||
$type = $embeddable->getType();
|
||||
if ($type === 'video' || $type === 'rich') {
|
||||
// Attempt to inherit width (but leave height auto)
|
||||
if (empty($arguments['width']) && $embeddable->getWidth()) {
|
||||
$arguments['width'] = $embeddable->getWidth();
|
||||
}
|
||||
return static::videoEmbed($arguments, $extractor->code->html);
|
||||
}
|
||||
if ($type === 'photo') {
|
||||
return static::photoEmbed($arguments, (string) $extractor->url);
|
||||
}
|
||||
if ($type === 'link') {
|
||||
return static::linkEmbed($arguments, (string) $extractor->url, $extractor->title);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Adapter $embed
|
||||
* @param array $arguments Additional shortcode params
|
||||
* @return string
|
||||
* @deprecated 4.11..5.0 Use embeddableToHtml instead
|
||||
*/
|
||||
public static function embedForTemplate($embed, $arguments)
|
||||
{
|
||||
Deprecation::notice('4.11', 'Use embeddableToHtml() instead');
|
||||
switch ($embed->getType()) {
|
||||
case 'video':
|
||||
case 'rich':
|
||||
|
134
tests/php/View/Embed/EmbedContainerTest.php
Normal file
134
tests/php/View/Embed/EmbedContainerTest.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use Embed\Extractor;
|
||||
use Embed\Http\Crawler;
|
||||
use SilverStripe\View\Embed\EmbedContainer;
|
||||
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
|
||||
|
||||
class EmbedContainerTest extends EmbedUnitTest
|
||||
{
|
||||
public function testGetDimensions()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertSame(480, $container->getWidth());
|
||||
$this->assertSame(270, $container->getHeight());
|
||||
$container = $this->getFallbackEmbedContainer();
|
||||
$this->assertSame(100, $container->getWidth());
|
||||
$this->assertSame(100, $container->getHeight());
|
||||
}
|
||||
|
||||
public function testGetPreviewURL()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertSame('https://www.youtube.com/watch?v=iRXJXaLV0n4', $container->getPreviewURL());
|
||||
$container = $this->getFallbackEmbedContainer();
|
||||
if (class_exists(AssetAdmin::class)) {
|
||||
$this->assertStringContainsString('client/dist/images/icon_file.png', $container->getPreviewURL());
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetName()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertSame('Try to stay SERIOUS -The most popular CAT videos', $container->getName());
|
||||
}
|
||||
|
||||
public function testGetType()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertSame('rich', $container->getType());
|
||||
$container = $this->getEmbedContainer(
|
||||
<<<EOT
|
||||
<video width="320" height="240" controls>
|
||||
<source src="movie.ogg" type="video/ogg">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
EOT
|
||||
);
|
||||
$this->assertSame('video', $container->getType());
|
||||
$container = $this->getEmbedContainer(
|
||||
<<<EOT
|
||||
<audio controls>
|
||||
<source src="horse.ogg" type="audio/ogg">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
EOT
|
||||
);
|
||||
$this->assertSame('audio', $container->getType());
|
||||
$container = $this->getEmbedContainer(
|
||||
<<<EOT
|
||||
<a data-flickr-embed="true" href="https://www.flickr.com/photos/philocycler/32119532132/"><img src="https://live.staticflickr.com/759/32119532132_50c3f7933f_b.jpg" width="1024" height="742" alt="bird"></a>
|
||||
EOT
|
||||
);
|
||||
$this->assertSame('photo', $container->getType());
|
||||
$container = $this->getEmbedContainer('<p>Lorem ipsum</p>');
|
||||
$this->assertSame('link', $container->getType());
|
||||
}
|
||||
|
||||
public function testValidate()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertTrue($container->validate());
|
||||
$container = $this->getFallbackEmbedContainer();
|
||||
$this->assertFalse($container->validate());
|
||||
}
|
||||
|
||||
public function testOptions()
|
||||
{
|
||||
$options = ['foo' => 'bar'];
|
||||
$container = $this->getEmbedContainer();
|
||||
$this->assertSame([], $container->getOptions());
|
||||
$container->setOptions($options);
|
||||
$this->assertSame($options, $container->getOptions());
|
||||
}
|
||||
|
||||
public function testGetExtractor()
|
||||
{
|
||||
$container = $this->getEmbedContainer();
|
||||
$extractor = $container->getExtractor();
|
||||
$this->assertTrue($extractor instanceof Extractor);
|
||||
$this->assertSame('Try to stay SERIOUS -The most popular CAT videos', $extractor->title);
|
||||
}
|
||||
|
||||
private function getFallbackEmbedContainer()
|
||||
{
|
||||
return $this->createEmbedContainer('', '', '', '');
|
||||
}
|
||||
|
||||
private function getEmbedContainer(string $htmlOverride = '')
|
||||
{
|
||||
$html = $htmlOverride ?: implode('', [
|
||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/iRXJXaLV0n4?feature=oembed" ',
|
||||
'frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>'
|
||||
]);
|
||||
$url = 'https://www.youtube.com/watch?v=iRXJXaLV0n4';
|
||||
return $this->createEmbedContainer(
|
||||
$url,
|
||||
$url,
|
||||
implode('', [
|
||||
'<html><link rel="alternate" type="application/json+oembed" ',
|
||||
'href="https://www.youtube.com/oembed?format=json&',
|
||||
'url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiRXJXaLV0n4" ',
|
||||
'title="Try to stay SERIOUS -The most popular CAT videos"></html>'
|
||||
]),
|
||||
json_encode([
|
||||
'author_url' => 'https://www.youtube.com/channel/UCR2KG2dK1tAkwZZjm7rAiSg',
|
||||
'thumbnail_width' => 480,
|
||||
'title' => 'Try to stay SERIOUS -The most popular CAT videos',
|
||||
'width' => 480,
|
||||
'provider_name' => 'YouTube',
|
||||
'author_name' => 'Tiger Funnies',
|
||||
'height' => 270,
|
||||
'version' => '1.0',
|
||||
'type' => 'video',
|
||||
// phpcs:ignore
|
||||
'html' => $html,
|
||||
'provider_url' => 'https://www.youtube.com/',
|
||||
'thumbnail_height' => 360,
|
||||
'thumbnail_url' => 'https://i.ytimg.com/vi/iRXJXaLV0n4/hqdefault.jpg',
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use Embed\Adapters\Adapter;
|
||||
use Embed\Http\DispatcherInterface;
|
||||
use Embed\Http\Response;
|
||||
use Embed\Http\Url;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\View\Embed\EmbedResource;
|
||||
|
||||
class EmbedResourceTest extends SapphireTest
|
||||
{
|
||||
public function testGetEmbed()
|
||||
{
|
||||
$dispatcherMock = $this->createMock(DispatcherInterface::class);
|
||||
$dispatcherMock->expects($this->atLeastOnce())->method('dispatch')->willReturn($this->mockResponse());
|
||||
|
||||
/** @var EmbedResource $embed */
|
||||
$embed = Injector::inst()->create(EmbedResource::class, 'https://www.youtube.com/watch?v=iRXJXaLV0n4');
|
||||
$this->assertEmpty($embed->getOptions());
|
||||
$this->assertEmpty($embed->getDispatcher());
|
||||
|
||||
$embed->setOptions(['foo' => 'bar']);
|
||||
$embed->setDispatcher($dispatcherMock);
|
||||
|
||||
$adapter = $embed->getEmbed();
|
||||
$this->assertInstanceOf(Adapter::class, $adapter);
|
||||
$this->assertSame('Try to stay SERIOUS -The most popular CAT videos', $adapter->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a mock Response object suitable for Embed
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
private function mockResponse()
|
||||
{
|
||||
$url = Url::create('https://www.youtube.com/watch?v=iRXJXaLV0n4');
|
||||
return new Response(
|
||||
$url,
|
||||
$url,
|
||||
200,
|
||||
'application/json',
|
||||
json_encode([
|
||||
'author_url' => 'https://www.youtube.com/channel/UCR2KG2dK1tAkwZZjm7rAiSg',
|
||||
'thumbnail_width' => 480,
|
||||
'title' => 'Try to stay SERIOUS -The most popular CAT videos',
|
||||
'width' => 480,
|
||||
'provider_name' => 'YouTube',
|
||||
'author_name' => 'Tiger Funnies',
|
||||
'height' => 270,
|
||||
'version' => '1.0',
|
||||
'type' => 'video',
|
||||
// phpcs:ignore
|
||||
'html' => '<iframe width="480" height="270" src="https://www.youtube.com/embed/iRXJXaLV0n4?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>',
|
||||
'provider_url' => 'https://www.youtube.com/',
|
||||
'thumbnail_height' => 360,
|
||||
'thumbnail_url' => 'https://i.ytimg.com/vi/iRXJXaLV0n4/hqdefault.jpg',
|
||||
]),
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
68
tests/php/View/Embed/EmbedUnitTest.php
Normal file
68
tests/php/View/Embed/EmbedUnitTest.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\View\Embed\EmbedContainer;
|
||||
use SilverStripe\View\Embed\Embeddable;
|
||||
use Embed\Http\Crawler;
|
||||
use Embed\Embed;
|
||||
|
||||
/**
|
||||
* Special unit test class to faciliate mock embed/embed requests
|
||||
*/
|
||||
class EmbedUnitTest extends SapphireTest
|
||||
{
|
||||
private bool $firstRequest = true;
|
||||
|
||||
public function getFirstRequest(): bool
|
||||
{
|
||||
return $this->firstRequest;
|
||||
}
|
||||
|
||||
public function setFirstRequest(bool $b): void
|
||||
{
|
||||
$this->firstRequest = $b;
|
||||
}
|
||||
|
||||
protected function createEmbedContainer(
|
||||
string $urlA,
|
||||
string $urlB,
|
||||
string $firstResponse,
|
||||
string $secondResponse
|
||||
): EmbedContainer {
|
||||
$this->registerCrawlerService($urlA, $urlB, $firstResponse, $secondResponse);
|
||||
$embedContainer = EmbedContainer::create($urlA);
|
||||
return $embedContainer;
|
||||
}
|
||||
|
||||
private function registerCrawlerService(
|
||||
string $urlA,
|
||||
string $urlB,
|
||||
string $firstResponse,
|
||||
string $secondResponse
|
||||
): void {
|
||||
$mockUriA = new MockUri($urlA);
|
||||
$mockUriB = new MockUri($urlB);
|
||||
$crawlerMock = $this->createMock(Crawler::class);
|
||||
$crawlerMock->method('getResponseUri')->willReturn($mockUriA);
|
||||
$crawlerMock->method('createUri')->willReturn($mockUriB);
|
||||
$crawlerMock->method('sendRequest')->willReturn(new MockResponse($this, $firstResponse, $secondResponse));
|
||||
$crawlerMock->method('createRequest')->willReturn(new MockRequest($this, $mockUriA));
|
||||
Injector::inst()->registerService($crawlerMock, Crawler::class);
|
||||
// replace the existing registered Embed singleton with a new singleton that is
|
||||
// created using $crawlerMock as the the __constructor argument - see oembed.yml
|
||||
$embed = Injector::inst()->create(Embed::class, $crawlerMock);
|
||||
Injector::inst()->registerService($embed, Embed::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is to prevent the following warning:
|
||||
* No tests found in class "SilverStripe\View\Tests\Embed\EmbedUnitTest".
|
||||
*/
|
||||
public function testPass()
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
97
tests/php/View/Embed/MockRequest.php
Normal file
97
tests/php/View/Embed/MockRequest.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
class MockRequest implements RequestInterface
|
||||
{
|
||||
private EmbedUnitTest $unitTest;
|
||||
private MockUri $mockUri;
|
||||
|
||||
public function __construct(EmbedUnitTest $unitTest, MockUri $mockUri)
|
||||
{
|
||||
$this->unitTest = $unitTest;
|
||||
$this->mockUri = $mockUri;
|
||||
}
|
||||
|
||||
public function getRequestTarget()
|
||||
{
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
{
|
||||
}
|
||||
|
||||
public function getUri()
|
||||
{
|
||||
$this->unitTest->setFirstRequest(false);
|
||||
return $this->mockUri;
|
||||
}
|
||||
|
||||
public function getProtocolVersion()
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeader($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeaderLine($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
{
|
||||
}
|
||||
|
||||
public function hasHeader($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function withHeader($name, $value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAddedHeader($name, $value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withoutHeader($name)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withBody(StreamInterface $body)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withProtocolVersion($version)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRequestTarget($requestTarget)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withMethod($method)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
90
tests/php/View/Embed/MockResponse.php
Normal file
90
tests/php/View/Embed/MockResponse.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
class MockResponse implements ResponseInterface
|
||||
{
|
||||
private EmbedUnitTest $unitTest;
|
||||
private string $firstReponse;
|
||||
private string $secondResponse;
|
||||
|
||||
public function __construct(EmbedUnitTest $unitTest, string $firstResponse, string $secondResponse)
|
||||
{
|
||||
$this->unitTest = $unitTest;
|
||||
$this->firstResponse = $firstResponse;
|
||||
$this->secondResponse = $secondResponse;
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
{
|
||||
// first request is to the video HTML to get to find the oembed link
|
||||
// second request is to the oembed endpoint to fetch JSON
|
||||
if ($this->unitTest->getFirstRequest()) {
|
||||
return $this->firstResponse;
|
||||
} else {
|
||||
return $this->secondResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public function getReasonPhrase()
|
||||
{
|
||||
}
|
||||
|
||||
public function getProtocolVersion()
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeader($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function getHeaderLine($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function hasHeader($name)
|
||||
{
|
||||
}
|
||||
|
||||
public function withHeader($name, $value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAddedHeader($name, $value)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withBody(StreamInterface $body)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withoutHeader($name)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withProtocolVersion($version)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatus($code, $reasonPhrase = '')
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
105
tests/php/View/Embed/MockUri.php
Normal file
105
tests/php/View/Embed/MockUri.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Embed;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
class MockUri implements UriInterface
|
||||
{
|
||||
private string $scheme;
|
||||
private string $host;
|
||||
private string $path;
|
||||
private string $query;
|
||||
|
||||
public function __construct(string $url)
|
||||
{
|
||||
$p = parse_url($url);
|
||||
$this->scheme = $p['scheme'] ?? '';
|
||||
$this->host = $p['host'] ?? '';
|
||||
$this->path = $p['path'] ?? '';
|
||||
$this->query = $p['query'] ?? '';
|
||||
}
|
||||
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function getPort()
|
||||
{
|
||||
}
|
||||
|
||||
public function getAuthority()
|
||||
{
|
||||
}
|
||||
|
||||
public function getUserInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public function getFragment()
|
||||
{
|
||||
}
|
||||
|
||||
public function withPath($path)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withScheme($scheme)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withUserInfo($user, $password = null)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withHost($host)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPort($port)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withQuery($query)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withFragment($fragment)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$query = $this->getQuery();
|
||||
return sprintf(
|
||||
'%s://%s%s%s',
|
||||
$this->getScheme(),
|
||||
$this->getHost(),
|
||||
'/' . ltrim($this->getPath(), '/'),
|
||||
$query ? "?$query" : ''
|
||||
);
|
||||
}
|
||||
}
|
@ -6,119 +6,154 @@ use Psr\SimpleCache\CacheInterface;
|
||||
use SilverStripe\View\Parsers\ShortcodeParser;
|
||||
use SilverStripe\View\Shortcodes\EmbedShortcodeProvider;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\View\Tests\Embed\EmbedUnitTest;
|
||||
|
||||
/**
|
||||
* Class EmbedShortcodeProviderTest
|
||||
*
|
||||
* Because Embed/Embed does not have a mockup, the tests have to run against a live environment.
|
||||
* I've tried to fix it by serializing the data to a file, but to no avail.
|
||||
* Any improvements on not having to call external resources are welcome.
|
||||
*/
|
||||
class EmbedShortcodeProviderTest extends SapphireTest
|
||||
class EmbedShortcodeProviderTest extends EmbedUnitTest
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string test youtube. The SilverStripe Platform promotion by UncleCheese
|
||||
*/
|
||||
protected static $test_youtube = 'https://www.youtube.com/watch?v=dM15HfUYwF0';
|
||||
|
||||
/**
|
||||
* @var string test Soundcloud. One of my favorite bands, Delain, Suckerpunch.
|
||||
*/
|
||||
protected static $test_soundcloud = 'http://soundcloud.com/napalmrecords/delain-suckerpunch';
|
||||
|
||||
public function assertEqualIgnoringWhitespace($a, $b, $message = '')
|
||||
{
|
||||
$this->assertEquals(preg_replace('/\s+/', '', $a), preg_replace('/\s+/', '', $b), $message);
|
||||
}
|
||||
|
||||
private function getShortcodeHtml(
|
||||
string $urlA,
|
||||
string $urlB,
|
||||
string $firstResponse,
|
||||
string $secondResponse,
|
||||
array $arguments
|
||||
): string {
|
||||
$firstResponse = str_replace("\n", '', $firstResponse);
|
||||
$secondResponse = str_replace("\n", '', $secondResponse);
|
||||
$embedContainer = $this->createEmbedContainer($urlA, $urlB, $firstResponse, $secondResponse);
|
||||
return EmbedShortcodeProvider::handle_shortcode($arguments, '', null, '', ['Embeddable' => $embedContainer]);
|
||||
}
|
||||
|
||||
public function testYoutube()
|
||||
{
|
||||
/** @var string $result */
|
||||
$result = $this->mockRequest(
|
||||
$url = 'https://www.youtube.com/watch?v=dM15HfUYwF0';
|
||||
$html = $this->getShortcodeHtml(
|
||||
$url,
|
||||
$url,
|
||||
<<<EOT
|
||||
<link rel="alternate" type="application/json+oembed" href="https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Da2tDOYkFCYo" title="The flying car completes first ever inter-city flight (Official Video)">
|
||||
EOT,
|
||||
<<<EOT
|
||||
{"title":"The flying car completes first ever inter-city flight (Official Video)","author_name":"KleinVision","author_url":"https://www.youtube.com/channel/UCCHAHvcO7KSNmgXVRIJLNkw","type":"video","height":113,"width":200,"version":"1.0","provider_name":"YouTube","provider_url":"https://www.youtube.com/","thumbnail_height":360,"thumbnail_width":480,"thumbnail_url":"https://i.ytimg.com/vi/a2tDOYkFCYo/hqdefault.jpg","html":"\u003ciframe width=\u0022200\u0022 height=\u0022113\u0022 src=\u0022https://www.youtube.com/embed/a2tDOYkFCYo?feature=oembed\u0022 frameborder=\u00220\u0022 allow=\u0022accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\u0022 allowfullscreen\u003e\u003c/iframe\u003e"}
|
||||
EOT,
|
||||
[
|
||||
'url' => static::$test_youtube,
|
||||
'url' => $url,
|
||||
'caption' => 'A nice video',
|
||||
'width' => 777,
|
||||
'height' => 437,
|
||||
],
|
||||
[
|
||||
'version' => '1.0',
|
||||
'provider_url' => 'https://www.youtube.com/',
|
||||
'title' => 'SilverStripe Platform 2 min introduction',
|
||||
'html' => '<iframe width="480" height="270" src="https://www.youtube.com/embed/dM15HfUYwF0?feature=oembed" frameborder="0" allowfullscreen></iframe>',
|
||||
'provider_name' => 'YouTube',
|
||||
'thumbnail_width' => 480,
|
||||
'type' => 'video',
|
||||
'thumbnail_url' => 'https://i.ytimg.com/vi/dM15HfUYwF0/hqdefault.jpg',
|
||||
'thumbnail_height' => 360,
|
||||
'width' => 480,
|
||||
'author_url' => 'https://www.youtube.com/user/SilverStripe',
|
||||
'author_name' => 'SilverStripe',
|
||||
'height' => 270,
|
||||
]
|
||||
);
|
||||
$this->assertEqualIgnoringWhitespace(
|
||||
<<<EOS
|
||||
<div style="width: 777px;"><iframe width="777" height="437" src="https://www.youtube.com/embed/dM15HfUYwF0?feature=oembed" frameborder="0" allowfullscreen></iframe>
|
||||
<p class="caption">A nice video</p></div>
|
||||
EOS
|
||||
,
|
||||
$result
|
||||
<<<EOT
|
||||
<div style="width:777px;"><iframe width="777" height="437" src="https://www.youtube.com/embed/a2tDOYkFCYo?feature=oembed" frameborder="0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe><p class="caption">A nice video</p></div>
|
||||
EOT,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
public function testSoundcloud()
|
||||
{
|
||||
/** @var string $result */
|
||||
$result = $this->mockRequest(
|
||||
['url' => static::$test_soundcloud],
|
||||
$url = 'https://soundcloud.com/napalmrecords/delain-suckerpunch';
|
||||
$html = $this->getShortcodeHtml(
|
||||
$url,
|
||||
$url,
|
||||
<<<EOT
|
||||
<link rel="alternate" type="text/json+oembed" href="https://soundcloud.com/oembed?url=https%3A%2F%2Fsoundcloud.com%2Fnapalmrecords%2Fdelain-suckerpunch&format=json">
|
||||
EOT,
|
||||
<<<EOT
|
||||
{"version":1.0,"type":"rich","provider_name":"SoundCloud","provider_url":"https://soundcloud.com","height":400,"width":"100%","title":"DELAIN - Suckerpunch by Napalm Records","description":"Taken from the EP \"Lunar Prelude\": https://shop.napalmrecords.com/delain","thumbnail_url":"https://i1.sndcdn.com/artworks-000143578557-af0v6l-t500x500.jpg","html":"<iframe width=\"100%\" height=\"400\" scrolling=\"no\" frameborder=\"no\" src=\"https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F242518079&show_artwork=true\"></iframe>","author_name":"Napalm Records","author_url":"https://soundcloud.com/napalmrecords"}
|
||||
EOT,
|
||||
[
|
||||
'version' => 1,
|
||||
'type' => 'rich',
|
||||
'provider_name' => 'SoundCloud',
|
||||
'provider_url' => 'http://soundcloud.com',
|
||||
'height' => 400,
|
||||
'width' => '100%',
|
||||
'title' => 'DELAIN - Suckerpunch by Napalm Records',
|
||||
'description' => 'Taken from the EP "Lunar Prelude": http://shop.napalmrecords.com/delain',
|
||||
'thumbnail_url' => 'http://i1.sndcdn.com/artworks-000143578557-af0v6l-t500x500.jpg',
|
||||
'html' => '<iframe width="100%" height="400" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?visual=true&url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F242518079&show_artwork=true"></iframe>',
|
||||
'author_name' => 'Napalm Records',
|
||||
'author_url' => 'http://soundcloud.com/napalmrecords',
|
||||
]
|
||||
'url' => $url
|
||||
],
|
||||
);
|
||||
$this->assertEqualIgnoringWhitespace(
|
||||
<<<EOS
|
||||
<div style="width: 100px;"><iframe width="100%" height="400" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?visual=true&url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F242518079&show_artwork=true"></iframe></div>
|
||||
EOS
|
||||
,
|
||||
$result
|
||||
<<<EOT
|
||||
<div style="width:100px;"><iframe width="100%" height="400" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F242518079&show_artwork=true"></iframe></div>
|
||||
EOT,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock an oembed request
|
||||
*
|
||||
* @param array $arguments Input arguments
|
||||
* @param array $response JSON response body
|
||||
* @return string
|
||||
*/
|
||||
protected function mockRequest($arguments, $response)
|
||||
public function testVimeo()
|
||||
{
|
||||
return EmbedShortcodeProvider::handle_shortcode(
|
||||
$arguments,
|
||||
'',
|
||||
null,
|
||||
'embed',
|
||||
$url = 'https://vimeo.com/680885625';
|
||||
$html = $this->getShortcodeHtml(
|
||||
$url,
|
||||
$url,
|
||||
<<<EOT
|
||||
<link rel="alternate" href="https://vimeo.com/api/oembed.json?url=https%3A%2F%2Fvimeo.com%2F680885625%3Fh%3D0cadf1a475" type="application/json+oembed" title="Mount Rainier National Park - 2021 - Episode 01">
|
||||
EOT,
|
||||
<<<EOT
|
||||
{"type":"video","version":"1.0","provider_name":"Vimeo","provider_url":"https:\/\/vimeo.com\/","title":"Mount Rainier National Park - 2021 - Episode 01","author_name":"Altered Stag Productions","author_url":"https:\/\/vimeo.com\/alteredstag","is_plus":"0","account_type":"pro","html":"<iframe src=\"https:\/\/player.vimeo.com\/video\/680885625?h=0cadf1a475&app_id=122963\" width=\"640\" height=\"360\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen title=\"Mount Rainier National Park - 2021 - Episode 01\"><\/iframe>","width":640,"height":360,"duration":60,"description":"Mount Rainier was the first national park I ever visited so it was definitely exciting to be back with refined skills and better equipment. Here is a quick cap of the trip with more segments on the way.\n\nSong: And What Now of the Birds for Ben by David Jennings - March 3, 2021.","thumbnail_url":"https:\/\/i.vimeocdn.com\/video\/1380153025-d3b1840ae521cd936bdaaafaef280b9c0634e729c6b09bca7767792b553a5220-d_640","thumbnail_width":640,"thumbnail_height":360,"thumbnail_url_with_play_button":"https:\/\/i.vimeocdn.com\/filter\/overlay?src0=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1380153025-d3b1840ae521cd936bdaaafaef280b9c0634e729c6b09bca7767792b553a5220-d_640&src1=http%3A%2F%2Ff.vimeocdn.com%2Fp%2Fimages%2Fcrawler_play.png","upload_date":"2022-02-23 08:54:15","video_id":680885625,"uri":"\/videos\/680885625"}
|
||||
EOT,
|
||||
[
|
||||
'resolver' => [
|
||||
'class' => MockResolver::class,
|
||||
'config' => [
|
||||
'expectedContent' => json_encode($response),
|
||||
'url' => $url
|
||||
],
|
||||
);
|
||||
$this->assertEqualIgnoringWhitespace(
|
||||
<<<EOT
|
||||
<div style="width: 640px;"><iframe src="https://player.vimeo.com/video/680885625?h=0cadf1a475&app_id=122963" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="Mount Rainier National Park - 2021 - Episode 01"></iframe></div>
|
||||
EOT,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlickr()
|
||||
{
|
||||
$urlA = 'https://www.flickr.com/photos/philocycler/32119532132/in/photolist-QWhZSL-DFFK9V-JcDYRD-S5ksMB-KPznfz-dT81te-2aqUUb1-Gur1ok-cgfEL1-dUu2Cv-8iqmZ9-z5ktAq-z5mCCE-9FmXnE-UH4Y1d-VZsXJn-22zGNHz-e1mzTR-22uVLSo-VJJWsE-VJJJQG-8in8np-agL5ae-9KKkAe-29if7Rt';
|
||||
$urlB = 'https://live.staticflickr.com/759/32119532132_50c3f7933f_b.jpg';
|
||||
$html = $this->getShortcodeHtml(
|
||||
$urlA,
|
||||
$urlB,
|
||||
<<<EOT
|
||||
<link rel="alternative" type="application/json+oembed" href="https://www.flickr.com/services/oembed?url=https://www.flickr.com/photos/philocycler/32119532132&format=json" data-dynamic-added-by="bb44774707b5780000000000000000000000000000000" data-dynamic="true" />
|
||||
EOT,
|
||||
<<<EOT
|
||||
{"type":"photo","flickr_type":"photo","title":"bird","author_name":"Philocycler","author_url":"https:\/\/www.flickr.com\/photos\/philocycler\/","width":1024,"height":742,"url":"https:\/\/live.staticflickr.com\/759\/32119532132_50c3f7933f_b.jpg","web_page":"https:\/\/www.flickr.com\/photos\/philocycler\/32119532132\/","thumbnail_url":"https:\/\/live.staticflickr.com\/759\/32119532132_50c3f7933f_q.jpg","thumbnail_width":150,"thumbnail_height":150,"web_page_short_url":"https:\/\/flic.kr\/p\/QWhZSL","license":"All Rights Reserved","license_id":0,"html":"<a data-flickr-embed=\"true\" href=\"https:\/\/www.flickr.com\/photos\/philocycler\/32119532132\/\" title=\"bird by Philocycler, on Flickr\"><img src=\"https:\/\/live.staticflickr.com\/759\/32119532132_50c3f7933f_b.jpg\" width=\"1024\" height=\"742\" alt=\"bird\"><\/a><script async src=\"https:\/\/embedr.flickr.com\/assets\/client-code.js\" charset=\"utf-8\"><\/script>","version":"1.0","cache_age":3600,"provider_name":"Flickr","provider_url":"https:\/\/www.flickr.com\/"}
|
||||
EOT,
|
||||
[
|
||||
'url' => $urlB,
|
||||
'width' => 1024,
|
||||
'height' => 742,
|
||||
'caption' => 'Birdy'
|
||||
],
|
||||
]
|
||||
);
|
||||
$this->assertEqualIgnoringWhitespace(
|
||||
<<<EOT
|
||||
<div style="width:1024px;"><a data-flickr-embed="true" href="https://www.flickr.com/photos/philocycler/32119532132/" title="birdbyPhilocycler,onFlickr"><img src="https://live.staticflickr.com/759/32119532132_50c3f7933f_b.jpg" width="1024" height="742" alt="bird"></a><script asyncsrc="https://embedr.flickr.com/assets/client-code.js" charset="utf-8"></script><p class="caption">Birdy</p></div>
|
||||
EOT,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
public function testAudio()
|
||||
{
|
||||
// not implemented in Silerstripe so will fallback to a link to $urlA
|
||||
$urlA = 'https://www.someaudioplace.com/12345';
|
||||
$urlB = 'https://www.someaudioplace.com/listen/12345';
|
||||
$html = $this->getShortcodeHtml(
|
||||
$urlA,
|
||||
$urlB,
|
||||
<<<EOT
|
||||
<link rel="alternative" type="application/json+oembed" href="https://www.someaudioplace.com/oembed?a=12345" data-dynamic="true" />
|
||||
EOT,
|
||||
<<<EOT
|
||||
{"type":"audio","title":"Some music","author_name":"bob","html":"<audio controls><source src="https://www.someaudioplace.com/listen/12345" type="audio/ogg"></audio>"}
|
||||
EOT,
|
||||
[
|
||||
'url' => $urlB,
|
||||
],
|
||||
);
|
||||
$this->assertEqualIgnoringWhitespace(
|
||||
<<<EOT
|
||||
<a href="https://www.someaudioplace.com/12345"></a>
|
||||
EOT,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\View\Tests\Shortcodes;
|
||||
|
||||
use Embed\Http\DispatcherInterface;
|
||||
use Embed\Http\ImageResponse;
|
||||
use Embed\Http\Response;
|
||||
use Embed\Http\Url;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MockResolver implements DispatcherInterface
|
||||
{
|
||||
protected $url = null;
|
||||
|
||||
protected $expectedContent = null;
|
||||
|
||||
/**
|
||||
* Constructor. Sets the url.
|
||||
*
|
||||
* @param string $url The url value
|
||||
* @param array $config The resolver configuration
|
||||
*/
|
||||
public function __construct($url, array $config)
|
||||
{
|
||||
$this->url = $url;
|
||||
if (empty($config['expectedContent'])) {
|
||||
throw new InvalidArgumentException("Mock resolvers need expectedContent");
|
||||
}
|
||||
$this->expectedContent = $config['expectedContent'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an url.
|
||||
*
|
||||
* @param Url $url
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function dispatch(Url $url)
|
||||
{
|
||||
return new Response(
|
||||
$url,
|
||||
$url,
|
||||
200,
|
||||
'application/json',
|
||||
$this->expectedContent,
|
||||
[],
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve multiple image urls at once.
|
||||
*
|
||||
* @param Url[] $urls
|
||||
*
|
||||
* @return ImageResponse[]
|
||||
*/
|
||||
public function dispatchImages(array $urls)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user