Add Schema.org ItemList support to YouTube index page
All checks were successful
Deploy to Server / deploy (push) Successful in 12s
All checks were successful
Deploy to Server / deploy (push) Successful in 12s
This commit is contained in:
parent
1f5e931af6
commit
60ac907163
2 changed files with 75 additions and 1 deletions
|
|
@ -1,4 +1,6 @@
|
||||||
|
import json
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
@ -41,6 +43,53 @@ class YouTubeIndexViewTest(TestCase):
|
||||||
'Blizzard, Fortnite, Riot Games, and more." />'
|
'Blizzard, Fortnite, Riot Games, and more." />'
|
||||||
) in content
|
) in content
|
||||||
|
|
||||||
|
def test_index_schema_data_is_valid_itemlist(self) -> None:
|
||||||
|
"""The page should include a valid Schema.org ItemList in the JSON-LD context."""
|
||||||
|
response: _MonkeyPatchedWSGIResponse = self.client.get(reverse("youtube:index"))
|
||||||
|
|
||||||
|
assert response.context is not None
|
||||||
|
assert "schema_data" in response.context
|
||||||
|
|
||||||
|
schema: dict[str, Any] = json.loads(response.context["schema_data"])
|
||||||
|
assert schema["@context"] == "https://schema.org"
|
||||||
|
assert schema["@type"] == "ItemList"
|
||||||
|
assert schema["name"] == "YouTube channels with rewards"
|
||||||
|
assert "itemListElement" in schema
|
||||||
|
|
||||||
|
items: list[dict[str, Any]] = schema["itemListElement"]
|
||||||
|
assert len(items) > 0
|
||||||
|
|
||||||
|
# Every entry must be a ListItem wrapping an Organization
|
||||||
|
for item in items:
|
||||||
|
assert item["@type"] == "ListItem"
|
||||||
|
assert "position" in item
|
||||||
|
org: dict[str, Any] = item["item"]
|
||||||
|
assert org["@type"] == "Organization"
|
||||||
|
assert "name" in org
|
||||||
|
assert isinstance(org["sameAs"], list)
|
||||||
|
assert len(org["sameAs"]) > 0
|
||||||
|
|
||||||
|
def test_index_schema_data_includes_known_orgs(self) -> None:
|
||||||
|
"""The Schema.org ItemList should contain entries for known organizations."""
|
||||||
|
response: _MonkeyPatchedWSGIResponse = self.client.get(reverse("youtube:index"))
|
||||||
|
schema: dict[str, Any] = json.loads(response.context["schema_data"]) # type: ignore[index]
|
||||||
|
org_names: list[str] = [
|
||||||
|
item["item"]["name"] for item in schema["itemListElement"]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert "Activision (Call of Duty)" in org_names
|
||||||
|
assert "Battle.net / Blizzard" in org_names
|
||||||
|
assert "Riot Games" in org_names
|
||||||
|
assert "Epic Games" in org_names
|
||||||
|
|
||||||
|
def test_index_schema_data_org_same_as_are_youtube_urls(self) -> None:
|
||||||
|
"""Each Organization's sameAs values should be YouTube URLs."""
|
||||||
|
response: _MonkeyPatchedWSGIResponse = self.client.get(reverse("youtube:index"))
|
||||||
|
schema: dict[str, Any] = json.loads(response.context["schema_data"]) # type: ignore[index]
|
||||||
|
for list_item in schema["itemListElement"]:
|
||||||
|
for url in list_item["item"]["sameAs"]:
|
||||||
|
assert url.startswith("https://www.youtube.com/")
|
||||||
|
|
||||||
def test_index_returns_200(self) -> None:
|
def test_index_returns_200(self) -> None:
|
||||||
"""The YouTube index page should return HTTP 200."""
|
"""The YouTube index page should return HTTP 200."""
|
||||||
response: _MonkeyPatchedWSGIResponse = self.client.get(reverse("youtube:index"))
|
response: _MonkeyPatchedWSGIResponse = self.client.get(reverse("youtube:index"))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
|
@ -116,9 +118,32 @@ def index(request: HttpRequest) -> HttpResponse:
|
||||||
"channels": sorted_items,
|
"channels": sorted_items,
|
||||||
})
|
})
|
||||||
|
|
||||||
context: dict[str, str | list[dict[str, str | list[dict[str, str]]]]] = {
|
list_items: list[dict[str, Any]] = [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": position,
|
||||||
|
"item": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": group["organization"],
|
||||||
|
"sameAs": [ch["url"] for ch in group["channels"]], # type: ignore[index]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for position, group in enumerate(organization_groups, start=1)
|
||||||
|
]
|
||||||
|
|
||||||
|
schema: dict[str, Any] = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ItemList",
|
||||||
|
"name": PAGE_TITLE,
|
||||||
|
"description": PAGE_DESCRIPTION,
|
||||||
|
"url": request.build_absolute_uri(),
|
||||||
|
"itemListElement": list_items,
|
||||||
|
}
|
||||||
|
|
||||||
|
context: dict[str, Any] = {
|
||||||
"page_title": PAGE_TITLE,
|
"page_title": PAGE_TITLE,
|
||||||
"page_description": PAGE_DESCRIPTION,
|
"page_description": PAGE_DESCRIPTION,
|
||||||
"organization_groups": organization_groups,
|
"organization_groups": organization_groups,
|
||||||
|
"schema_data": json.dumps(schema),
|
||||||
}
|
}
|
||||||
return render(request=request, template_name="youtube/index.html", context=context)
|
return render(request=request, template_name="youtube/index.html", context=context)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue