From 3bea69f7c1b59ad42411c578acc97e7f5ea68523 Mon Sep 17 00:00:00 2001 From: Christian Einvik Date: Thu, 12 Oct 2023 15:16:24 +0200 Subject: [PATCH] Fix missing H5P content type icons caused by h5p-editor library downgrade. Add tests --- .../app/H5PLibrariesHubCache.php | 3 + .../app/H5PLibraryCapability.php | 3 + sourcecode/apis/contentauthor/composer.lock | 8 +- .../factories/H5PLibrariesHubCacheFactory.php | 43 +++++ .../factories/H5PLibraryCapabilityFactory.php | 24 +++ .../Libraries/H5P/AjaxRequestTest.php | 168 ++++++++++++++++++ 6 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 sourcecode/apis/contentauthor/database/factories/H5PLibrariesHubCacheFactory.php create mode 100644 sourcecode/apis/contentauthor/database/factories/H5PLibraryCapabilityFactory.php diff --git a/sourcecode/apis/contentauthor/app/H5PLibrariesHubCache.php b/sourcecode/apis/contentauthor/app/H5PLibrariesHubCache.php index 722ed01c64..e40ae7f795 100644 --- a/sourcecode/apis/contentauthor/app/H5PLibrariesHubCache.php +++ b/sourcecode/apis/contentauthor/app/H5PLibrariesHubCache.php @@ -3,6 +3,7 @@ namespace App; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -16,6 +17,8 @@ class H5PLibrariesHubCache extends Model { + use HasFactory; + protected $table = 'h5p_libraries_hub_cache'; protected $guarded = []; diff --git a/sourcecode/apis/contentauthor/app/H5PLibraryCapability.php b/sourcecode/apis/contentauthor/app/H5PLibraryCapability.php index baab8608d3..870e092d95 100644 --- a/sourcecode/apis/contentauthor/app/H5PLibraryCapability.php +++ b/sourcecode/apis/contentauthor/app/H5PLibraryCapability.php @@ -3,12 +3,15 @@ namespace App; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\Lang; class H5PLibraryCapability extends Model { + use HasFactory; + protected $table = 'h5p_library_capabilities'; protected $appends = ['title', 'description']; diff --git a/sourcecode/apis/contentauthor/composer.lock b/sourcecode/apis/contentauthor/composer.lock index d564bccd23..76da042a6e 100644 --- a/sourcecode/apis/contentauthor/composer.lock +++ b/sourcecode/apis/contentauthor/composer.lock @@ -2528,12 +2528,12 @@ "source": { "type": "git", "url": "https://github.com/h5p/h5p-editor-php-library.git", - "reference": "1ae19fdb80839b32dad3846d6b0a5c745f8f6187" + "reference": "0365b081efa8b55ab9fd58594aa599f9630268f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/h5p/h5p-editor-php-library/zipball/1ae19fdb80839b32dad3846d6b0a5c745f8f6187", - "reference": "1ae19fdb80839b32dad3846d6b0a5c745f8f6187", + "url": "https://api.github.com/repos/h5p/h5p-editor-php-library/zipball/0365b081efa8b55ab9fd58594aa599f9630268f6", + "reference": "0365b081efa8b55ab9fd58594aa599f9630268f6", "shasum": "" }, "require": { @@ -2582,7 +2582,7 @@ "issues": "https://github.com/h5p/h5p-editor-php-library/issues", "source": "https://github.com/h5p/h5p-editor-php-library/tree/master" }, - "time": "2022-06-02T06:11:10+00:00" + "time": "2023-08-17T05:55:25+00:00" }, { "name": "kamermans/guzzle-oauth2-subscriber", diff --git a/sourcecode/apis/contentauthor/database/factories/H5PLibrariesHubCacheFactory.php b/sourcecode/apis/contentauthor/database/factories/H5PLibrariesHubCacheFactory.php new file mode 100644 index 0000000000..3c63dc316e --- /dev/null +++ b/sourcecode/apis/contentauthor/database/factories/H5PLibrariesHubCacheFactory.php @@ -0,0 +1,43 @@ + + */ +class H5PLibrariesHubCacheFactory extends Factory +{ + public function definition(): array + { + return [ + 'name' => 'H5P.Foobar', + 'major_version' => 2, + 'minor_version' => 4, + 'patch_version' => 6, + 'h5p_major_version' => 1, + 'h5p_minor_version' => 25, + 'title' => $this->faker->words(3, true), + 'summary' => $this->faker->sentence, + 'description' => $this->faker->sentences(3, true), + 'icon' => $this->faker->url . '/icon.svg', + 'is_recommended' => 0, + 'popularity' => $this->faker->numberBetween(0, 100000), + 'screenshots' => json_encode([ + $this->faker->url . '/image01.jpg', + $this->faker->url . '/image02.jpg', + $this->faker->url . '/image03.jpg', + ]), + 'license' => '{"id":"MIT","attributes":{"useCommercially":true,"modifiable":true,"distributable":true,"sublicensable":true,"canHoldLiable":false,"mustIncludeCopyright":true,"mustIncludeLicense":true}}', + 'example' => $this->faker->url, + 'tutorial' => $this->faker->url, + 'keywords' => json_encode($this->faker->words()), + 'categories' => '["Other"]', + 'owner' => $this->faker->userName, + ]; + } +} diff --git a/sourcecode/apis/contentauthor/database/factories/H5PLibraryCapabilityFactory.php b/sourcecode/apis/contentauthor/database/factories/H5PLibraryCapabilityFactory.php new file mode 100644 index 0000000000..116cd21aba --- /dev/null +++ b/sourcecode/apis/contentauthor/database/factories/H5PLibraryCapabilityFactory.php @@ -0,0 +1,24 @@ + + */ +class H5PLibraryCapabilityFactory extends Factory +{ + public function definition(): array + { + return [ + 'library_id' => $this->faker->numberBetween(), + 'name' => 'H5P.Foobar 1.2', + 'score' => 0, + 'enabled' => 1, + ]; + } +} diff --git a/sourcecode/apis/contentauthor/tests/Integration/Libraries/H5P/AjaxRequestTest.php b/sourcecode/apis/contentauthor/tests/Integration/Libraries/H5P/AjaxRequestTest.php index ba807412a1..456ad64a45 100644 --- a/sourcecode/apis/contentauthor/tests/Integration/Libraries/H5P/AjaxRequestTest.php +++ b/sourcecode/apis/contentauthor/tests/Integration/Libraries/H5P/AjaxRequestTest.php @@ -2,9 +2,13 @@ namespace Tests\Integration\Libraries\H5P; +use App\H5PLibrariesHubCache; use App\H5PLibrary; +use App\H5PLibraryCapability; use App\Libraries\H5P\AjaxRequest; +use Generator; use Illuminate\Foundation\Testing\RefreshDatabase; +use Storage; use Tests\TestCase; class AjaxRequestTest extends TestCase @@ -61,4 +65,168 @@ public function test_libraryRebuild(): void 'dependency_type' => 'editor', ]); } + + /** @dataProvider provider_contentTypeCache_icon */ + public function test_contentTypeCache_icon(array $libraryData, string $iconPath): void + { + Storage::fake('test'); + + $library = H5PLibrary::factory()->create($libraryData); + + H5PLibraryCapability::factory()->create([ + 'library_id' => $library->id, + ]); + + if ($iconPath !== '') { + Storage::put($iconPath, 'icon content'); + } + + $content = $this + ->post('/ajax', ['action' => \H5PEditorEndpoints::CONTENT_TYPE_CACHE, 'h5p_id' => '']) + ->assertOk() + ->assertJsonStructure([ + 'outdated', + 'libraries', + 'recentlyUsed', + 'apiVersion' => [ + 'major', + 'minor', + ], + 'details', + ]) + ->decodeResponseJson(); + + $libraries = $content['libraries']; + $this->assertCount(1, $libraries); + $libData = $libraries[0]; + + $this->assertSame($library->name, $libData['machineName']); + + if ($library->has_icon) { + $this->assertStringContainsString($iconPath, $libData['icon']); + } else { + $this->assertArrayNotHasKey('icon', $libData); + } + } + + public function provider_contentTypeCache_icon(): Generator + { + yield 'No patch with icon' => [ + [ + 'has_icon' => true, + 'patch_version_in_folder_name' => false, + ], + 'libraries/H5P.Foobar-1.2/icon.svg', + ]; + + yield 'Patch with icon' => [ + [ + 'has_icon' => true, + 'patch_version_in_folder_name' => true, + ], + 'libraries/H5P.Foobar-1.2.3/icon.svg', + ]; + + yield 'Missing icon' => [ + [ + 'has_icon' => true, + 'patch_version_in_folder_name' => true, + ], + '', + ]; + + yield 'No icon' => [ + [ + 'has_icon' => false, + 'patch_version_in_folder_name' => true, + ], + 'libraries/H5P.Foobar-1.2.3/icon.svg', + ]; + } + + /** @dataProvider provider_contentTypeCache_LocalAndCache */ + public function test_contentTypeCache_localAndCache(bool $usePatchVersion): void + { + Storage::fake('test'); + + $localOnlyLibrary = H5PLibrary::factory()->create([ + 'name' => 'H5P.Snafu', + 'has_icon' => true, + 'patch_version_in_folder_name' => $usePatchVersion, + ]); + H5PLibraryCapability::factory()->create([ + 'library_id' => $localOnlyLibrary->id, + ]); + + $library = H5PLibrary::factory()->create([ + 'patch_version_in_folder_name' => $usePatchVersion, + ]); + H5PLibraryCapability::factory()->create([ + 'library_id' => $library->id, + ]); + + $hubLibrary = H5PLibrariesHubCache::factory()->create(); + + $content = $this + ->withSession(['isAdmin' => true]) + ->post('/ajax', ['action' => \H5PEditorEndpoints::CONTENT_TYPE_CACHE, 'h5p_id' => '']) + ->assertOk() + ->assertJsonStructure([ + 'outdated', + 'libraries', + 'recentlyUsed', + 'apiVersion' => [ + 'major', + 'minor', + ], + 'details', + ]) + ->decodeResponseJson(); + + $this->assertCount(2, $content['libraries']); + + $libData = $content['libraries'][0]; + $this->assertSame($hubLibrary->id, $libData['id']); + $this->assertSame($hubLibrary->title, $libData['title']); + $this->assertSame($hubLibrary->major_version, $libData['majorVersion']); + $this->assertSame($hubLibrary->minor_version, $libData['minorVersion']); + $this->assertSame($hubLibrary->patch_version, $libData['patchVersion']); + $this->assertSame($library->major_version, $libData['localMajorVersion']); + $this->assertSame($library->minor_version, $libData['localMinorVersion']); + $this->assertSame($library->patch_version, $libData['localPatchVersion']); + $this->assertTrue($libData['installed']); + $this->assertFalse($libData['isUpToDate']); + $this->assertFalse($libData['restricted']); + $this->assertFalse($libData['canInstall']); + $this->assertArrayHasKey('summary', $libData); + $this->assertArrayHasKey('isRecommended', $libData); + $this->assertArrayHasKey('popularity', $libData); + $this->assertArrayHasKey('screenshots', $libData); + $this->assertArrayHasKey('license', $libData); + + $libData = $content['libraries'][1]; + $this->assertSame($localOnlyLibrary->id, $libData['id']); + $this->assertSame($localOnlyLibrary->title, $libData['title']); + $this->assertSame($localOnlyLibrary->major_version, $libData['majorVersion']); + $this->assertSame($localOnlyLibrary->minor_version, $libData['minorVersion']); + $this->assertSame($localOnlyLibrary->patch_version, $libData['patchVersion']); + $this->assertSame($localOnlyLibrary->major_version, $libData['localMajorVersion']); + $this->assertSame($localOnlyLibrary->minor_version, $libData['localMinorVersion']); + $this->assertSame($localOnlyLibrary->patch_version, $libData['localPatchVersion']); + $this->assertTrue($libData['installed']); + $this->assertTrue($libData['isUpToDate']); + $this->assertFalse($libData['restricted']); + $this->assertFalse($libData['canInstall']); + $this->assertArrayNotHasKey('summary', $libData); + $this->assertArrayNotHasKey('isRecommended', $libData); + $this->assertArrayNotHasKey('popularity', $libData); + $this->assertArrayNotHasKey('screenshots', $libData); + $this->assertArrayNotHasKey('license', $libData); + } + + public function provider_contentTypeCache_localAndCache(): Generator + { + yield 'no patch version' => [false]; + yield 'patch version' => [true]; + } }