diff --git a/funnel/assets/js/utils/embedvideo.js b/funnel/assets/js/utils/embedvideo.js index b02b0d743..29d50255c 100644 --- a/funnel/assets/js/utils/embedvideo.js +++ b/funnel/assets/js/utils/embedvideo.js @@ -6,34 +6,63 @@ const Video = { The videoID is then used to generate the iframe html. The generated iframe is added to the video container element. */ - getVideoTypeAndId(url) { - const regexMatch = url.match( - /(http:|https:|)\/\/(player.|www.)?(y2u\.be|vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|live\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(&\S+)?/ - ); - let type = ''; - if (regexMatch && regexMatch.length > 5) { - if (regexMatch[3].indexOf('youtu') > -1 || regexMatch[3].indexOf('y2u') > -1) { - type = 'youtube'; - } else if (regexMatch[3].indexOf('vimeo') > -1) { + validHostnames: [ + 'www.youtube.com', + 'youtube.com', + 'youtu.be', + 'y2u.be', + 'www.vimeo.com', + 'vimeo.com', + 'player.vimeo.com', + ], + getVideoTypeAndId(videoUrl) { + let videoId; + let paramId; + let type; + const url = new URL(videoUrl); + const { hostname } = url; + let regexMatch; + if (this.validHostnames.includes(hostname)) { + if (hostname.includes('vimeo')) { type = 'vimeo'; + paramId = url.searchParams.get('h'); + if (paramId) { + regexMatch = url.pathname.match(/\/(video\/)?(?[A-Za-z0-9._%-]*)/); + videoId = regexMatch.groups.videoId; + } else { + regexMatch = url.pathname.match( + /\/(video\/)?(?[A-Za-z0-9._%-]*)?(\/)?(?[A-Za-z0-9._%-]*)/ + ); + videoId = regexMatch.groups.videoId; + paramId = regexMatch.groups.paramId; + } + } else { + type = 'youtube'; + videoId = url.searchParams.get('v'); + if (!videoId) { + regexMatch = url.pathname.match( + /\/(embed\/|live\/)?(?[A-Za-z0-9._%-]*)/ + ); + videoId = regexMatch.groups.videoId; + } } return { type, - videoId: regexMatch[6], + videoId, + paramId, }; } - return { - type, - videoId: url, - }; + return {}; }, embedIframe(videoWrapper, videoUrl) { let videoEmbedUrl = ''; - const { type, videoId } = this.getVideoTypeAndId(videoUrl); + const { type, videoId, paramId } = this.getVideoTypeAndId(videoUrl); if (type === 'youtube') { videoEmbedUrl = ``; } else if (type === 'vimeo') { - videoEmbedUrl = ``; + videoEmbedUrl = ``; } if (videoEmbedUrl) { videoWrapper.innerHTML = videoEmbedUrl; diff --git a/funnel/forms/project.py b/funnel/forms/project.py index 633808ce0..c12284369 100644 --- a/funnel/forms/project.py +++ b/funnel/forms/project.py @@ -167,6 +167,7 @@ class ProjectLivestreamForm(forms.Form): 'y2u.be', 'www.vimeo.com', 'vimeo.com', + 'player.vimeo.com', ), message_schemes=__("A https:// URL is required"), message_domains=__("Livestream must be on YouTube or Vimeo"), diff --git a/tests/e2e/account/register_test.py b/tests/e2e/account/register_test.py index bda6b8810..2200a5b67 100644 --- a/tests/e2e/account/register_test.py +++ b/tests/e2e/account/register_test.py @@ -14,7 +14,13 @@ from ...conftest import scoped_session scenarios('account/register.feature') -pytestmark = pytest.mark.usefixtures('live_server') +pytestmark = [ + pytest.mark.usefixtures('live_server'), + pytest.mark.filterwarnings( + "ignore:Object of type not in session", + "ignore:Object of type not in session", + ), +] TWOFLOWER_EMAIL = 'twoflower@example.org' TWOFLOWER_PHONE = '+12015550123' @@ -22,11 +28,6 @@ ANONYMOUS_PHONE = '8123456789' ANONYMOUS_EMAIL = 'anon@example.com' -pytestmark = pytest.mark.filterwarnings( - "ignore:Object of type not in session", - "ignore:Object of type not in session", -) - @pytest.fixture() def published_project( diff --git a/tests/e2e/basic/video_test.py b/tests/e2e/basic/video_test.py new file mode 100644 index 000000000..84fa703ef --- /dev/null +++ b/tests/e2e/basic/video_test.py @@ -0,0 +1,65 @@ +"""Test livestream urls.""" + +from playwright.sync_api import Page, expect + +from funnel import models + +from ...conftest import scoped_session + +VETINARI_EMAIL = 'vetinari@example.org' +VETINARI_PASSWORD = 've@pwd3289' # nosec + + +def wait_until_recaptcha_loaded(page: Page) -> None: + page.wait_for_selector( + '#form-passwordlogin > div.g-recaptcha > div > div.grecaptcha-logo > iframe', + timeout=10000, + ) + + +def test_login_add_livestream( + db_session: scoped_session, + live_server, + user_vetinari, + project_expo2010: models.Project, + page: Page, +): + user_vetinari.add_email(VETINARI_EMAIL) + user_vetinari.password = VETINARI_PASSWORD + db_session.commit() + page.goto(live_server.url) + page.get_by_role("link", name="Login").click() + wait_until_recaptcha_loaded(page) + page.wait_for_selector('input[name=username]').fill(VETINARI_EMAIL) + page.click('#use-password-login') + page.wait_for_selector('input[name=password]').fill(VETINARI_PASSWORD) + page.click('#login-btn') + assert ( + page.wait_for_selector('.alert__text').inner_text() == "You are now logged in" + ) + page.goto(project_expo2010.absolute_url) + page.get_by_label("Update livestream URLs").click() + page.get_by_label("Livestream URLs. One per line").click() + page.get_by_label("Livestream URLs. One per line").fill( + "https://www.youtube.com/watch?v=dQw4w9WgXcQ\nhttps://vimeo.com/336892869\nhttps://player.vimeo.com/video/860038461?h=87fb31038b" + ) + page.get_by_role("button", name="Save changes").click() + expect( + page.frame_locator( + "internal:role=tabpanel[name=\"Livestream\"i] >> iframe" + ).get_by_label("YouTube Video Player") + ).to_contain_text("Rick Astley - Never Gonna Give You Up (Official Music Video)") + page.get_by_role("tab", name="Livestream 2").click() + expect( + page.frame_locator( + "internal:role=tabpanel[name=\"Livestream\"i] >> iframe" + ).get_by_role("banner") + ).to_contain_text("Rick Astley - Never Gonna Give You Up (Video)") + page.get_by_role("tab", name="Livestream 3").click() + expect( + page.frame_locator( + "internal:role=tabpanel[name=\"Livestream\"i] >> iframe" + ).get_by_role("banner") + ).to_contain_text( + "Practical SLSA for developers and application security professionals" + ) diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index c10044114..71587f01e 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -3,7 +3,6 @@ # pylint: disable=redefined-outer-name import pytest -from pytest_socket import enable_socket from sqlalchemy.orm import scoped_session @@ -13,5 +12,7 @@ def db_session(db_session_truncate: scoped_session) -> scoped_session: return db_session_truncate -def pytest_runtest_setup() -> None: - enable_socket() +def pytest_collection_modifyitems(items): + for item in items: + if 'live_server' in getattr(item, 'fixturenames', ()): + item.add_marker(pytest.mark.enable_socket())