diff --git a/playwright.config.ts b/playwright.config.ts index 047b5a13e..9d001833f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -29,6 +29,11 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', }, + timeout: 60 * 1000, + globalTimeout: 60 * 1000, + expect: { + timeout: 60 * 1000, + }, /* Configure projects for major browsers */ projects: [ diff --git a/src/components/NavItems/Assistant/AssistantRuleBook.jsx b/src/components/NavItems/Assistant/AssistantRuleBook.jsx index 10f6cf5dd..5a628ffc2 100644 --- a/src/components/NavItems/Assistant/AssistantRuleBook.jsx +++ b/src/components/NavItems/Assistant/AssistantRuleBook.jsx @@ -1,3 +1,4 @@ +import React from "react"; import analysisIconOff from "../../NavBar/images/tools/video_logoOff.png"; import keyframesIconOff from "../../NavBar/images/tools/keyframesOff.png"; import thumbnailsIconOff from "../../NavBar/images/tools/youtubeOff.png"; @@ -6,6 +7,7 @@ import metadataIconOff from "../../NavBar/images/tools/metadataOff.png"; import videoRightsIconOff from "../../NavBar/images/tools/copyrightOff.png"; import forensicIconOff from "../../NavBar/images/tools/forensic_logoOff.png"; import videoIconOff from "../../NavBar/images/tools/video_logoOff.png"; +import DownloadIcon from "@mui/icons-material/Download"; export const NE_SUPPORTED_LANGS = ["en", "pt", "fr", "de", "el", "es", "it"]; @@ -113,11 +115,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_analysis_video", icon: analysisIconOff, - linksAccepted: [ - KNOWN_LINKS.YOUTUBE, - KNOWN_LINKS.FACEBOOK, - KNOWN_LINKS.TWITTER, - ], + linksAccepted: [KNOWN_LINKS.YOUTUBE, KNOWN_LINKS.FACEBOOK], cTypes: [CONTENT_TYPE.VIDEO], exceptions: [], useInputUrl: true, @@ -142,14 +140,10 @@ export const ASSISTANT_ACTIONS = [ linksAccepted: [ KNOWN_LINKS.YOUTUBE, KNOWN_LINKS.FACEBOOK, - KNOWN_LINKS.TWITTER, - KNOWN_LINKS.DAILYMOTION, - KNOWN_LINKS.VIMEO, KNOWN_LINKS.YOUTUBE, KNOWN_LINKS.YOUTUBESHORTS, KNOWN_LINKS.LIVELEAK, KNOWN_LINKS.OWN, - KNOWN_LINKS.INSTAGRAM, ], cTypes: [CONTENT_TYPE.VIDEO], exceptions: [], @@ -172,7 +166,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_magnifier", icon: magnifierIconOff, - linksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], + processLinksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], cTypes: [CONTENT_TYPE.IMAGE], exceptions: [], useInputUrl: false, @@ -183,7 +177,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_metadata", icon: metadataIconOff, - linksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], + processLinksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], cTypes: [CONTENT_TYPE.IMAGE, CONTENT_TYPE.VIDEO], exceptions: [ /(pbs.twimg.com)|(youtu.be|youtube)|(instagram)|(fbcdn.net)|(vimeo)|(tiktok.com)/, @@ -196,11 +190,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_rights", icon: videoRightsIconOff, - linksAccepted: [ - KNOWN_LINKS.YOUTUBE, - KNOWN_LINKS.FACEBOOK, - KNOWN_LINKS.TWITTER, - ], + linksAccepted: [KNOWN_LINKS.YOUTUBE], cTypes: [CONTENT_TYPE.VIDEO], exceptions: [], useInputUrl: true, @@ -211,7 +201,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_forensic", icon: forensicIconOff, - linksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], + processLinksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], cTypes: [CONTENT_TYPE.IMAGE], exceptions: [], useInputUrl: false, @@ -222,7 +212,7 @@ export const ASSISTANT_ACTIONS = [ { title: "navbar_ocr", icon: forensicIconOff, - linksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], + processLinksAccepted: [KNOWN_LINKS.MISC, KNOWN_LINKS.OWN], cTypes: [CONTENT_TYPE.IMAGE], exceptions: [], useInputUrl: false, @@ -233,7 +223,11 @@ export const ASSISTANT_ACTIONS = [ { title: "assistant_video_download_action", icon: videoIconOff, - linksAccepted: [KNOWN_LINKS.TELEGRAM, KNOWN_LINKS.MISC], + linksAccepted: [ + KNOWN_LINKS.TELEGRAM, + KNOWN_LINKS.FACEBOOK, + KNOWN_LINKS.TWITTER, + ], cTypes: [CONTENT_TYPE.VIDEO], exceptions: [], useInputUrl: false, @@ -241,6 +235,36 @@ export const ASSISTANT_ACTIONS = [ tsvPrefix: "assistant_video", download: true, }, + { + title: "assistant_video_download_generic", + icon: , + linksAccepted: [ + KNOWN_LINKS.YOUTUBESHORTS, + KNOWN_LINKS.INSTAGRAM, + KNOWN_LINKS.FACEBOOK, + KNOWN_LINKS.VK, + KNOWN_LINKS.VIMEO, + KNOWN_LINKS.LIVELEAK, + KNOWN_LINKS.DAILYMOTION, + ], + cTypes: [CONTENT_TYPE.VIDEO], + exceptions: [], + useInputUrl: false, + text: "assistant_video_download_generic_description", + tsvPrefix: "assistant_video", + path: null, + }, + { + title: "assistant_video_download_tiktok", + icon: , + linksAccepted: [KNOWN_LINKS.TIKTOK], + cTypes: [CONTENT_TYPE.VIDEO], + exceptions: [], + useInputUrl: false, + text: "assistant_video_download_tiktok_description", + tsvPrefix: "assistant_video", + path: null, + }, ]; export const selectCorrectActions = ( @@ -251,12 +275,10 @@ export const selectCorrectActions = ( ) => { let possibleActions = ASSISTANT_ACTIONS.filter( (action) => - ((action.useInputUrl && - action.linksAccepted.includes(inputUrlType) && - action.cTypes.includes(contentType)) || - (!action.useInputUrl && - action.linksAccepted.includes(processUrlType) && - action.cTypes.includes(contentType))) && + (!action.linksAccepted || action.linksAccepted.includes(inputUrlType)) && + (!action.processLinksAccepted || + action.processLinksAccepted.includes(processUrlType)) && + action.cTypes.includes(contentType) && (action.exceptions.length === 0 || !processUrl.match(action.exceptions)), ); return possibleActions; diff --git a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantImageResult.jsx b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantImageResult.jsx index ed3da6dcd..348130ade 100644 --- a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantImageResult.jsx +++ b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantImageResult.jsx @@ -32,6 +32,7 @@ const AssistantImageResult = () => { height={"100%"} alt={processUrl} width={"100%"} + data-testid="assistant-media-image" /> diff --git a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantProcessUrlActions.jsx b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantProcessUrlActions.jsx index 2376f6942..c13cc0aa4 100644 --- a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantProcessUrlActions.jsx +++ b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantProcessUrlActions.jsx @@ -36,7 +36,9 @@ const AssistantProcessUrlActions = () => { dl.setAttribute("href", resultUrl); dl.setAttribute("download", ""); dl.click(); - } else if (resultUrl != null) { + } else if (action.path === null) { + return; // Do nothing if path is null + } else if (resultUrl !== null) { navigate( "/app/" + action.path + @@ -70,19 +72,31 @@ const AssistantProcessUrlActions = () => { handleClick(action)}> - + {typeof action.icon === "string" && ( + + )} + {typeof action.icon !== "string" && action.icon} - + {keyword(action.title)} } secondary={ - {keyword(action.text)} + + + } /> diff --git a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantVideoResult.jsx b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantVideoResult.jsx index 3fcfc9d44..11954d0ab 100644 --- a/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantVideoResult.jsx +++ b/src/components/NavItems/Assistant/AssistantScrapeResults/AssistantVideoResult.jsx @@ -51,6 +51,11 @@ const AssistantVideoResult = () => { let stringToMatch = ""; let positionOne = 0; + // Don't embed blob links, they are url for cached in-memory video + if (embedURL.startsWith("blob:")) { + return null; + } + switch (input_url_type) { case KNOWN_LINKS.YOUTUBE: if (!embedURL.includes("/embed/")) { @@ -83,6 +88,9 @@ const AssistantVideoResult = () => { "embed/" + embedURL.slice(positionOne); break; + case KNOWN_LINKS.TIKTOK: + embedURL = null; + break; default: return embedURL; } @@ -95,16 +103,18 @@ const AssistantVideoResult = () => { return ( - + {useIframe() && preprocessLinkForEmbed(processUrl) && ( - + + + )} {!useIframe() && preprocessLinkForEmbed(processUrl) && ( { controls={true} height="400" width="100%" + data-testid="assistant-media-video-tag" /> )} {!preprocessLinkForEmbed(processUrl) && ( @@ -125,7 +136,9 @@ const AssistantVideoResult = () => { justifyContent: "center", }} > - {keyword("embedding_not_supported")} + + {keyword("embedding_not_supported")} + )} @@ -138,7 +151,6 @@ const AssistantVideoResult = () => { {keyword("download_video")} - currentIndex) { @@ -48,15 +48,15 @@ function treeMapToElementsRecursive( hSpanStart < span.start ? span.start : hSpanStart; const boundedEnd = hSpanEnd > span.end ? span.end : hSpanEnd; if (wrapFunc) { - console.log("Wrapping: ", text.substring(boundedStart, boundedEnd)); + // console.log("Wrapping: ", text.substring(boundedStart, boundedEnd)); childElems.push( wrapFunc(text.substring(boundedStart, boundedEnd), hSpan), ); } else { - console.log( - "Not wrapping: ", - text.substring(boundedStart, boundedEnd), - ); + // console.log( + // "Not wrapping: ", + // text.substring(boundedStart, boundedEnd), + // ); childElems.push(text.substring(boundedStart, boundedEnd)); } diff --git a/src/components/Shared/ImageGridList/ImageGridList.jsx b/src/components/Shared/ImageGridList/ImageGridList.jsx index 97d04c34f..866b85126 100644 --- a/src/components/Shared/ImageGridList/ImageGridList.jsx +++ b/src/components/Shared/ImageGridList/ImageGridList.jsx @@ -36,6 +36,7 @@ const ImageImageList = (props) => { onClick={() => props.handleClick(props.list[index])} onLoad={props.setLoading} style={{ width: "100%", height: "auto" }} + data-testid={"assistant-media-grid-image-" + index} /> ) : ( { alt={tile} onClick={() => props.handleClick(props.list[index])} style={{ width: "100%", height: "auto" }} + data-testid={"assistant-media-grid-image-" + index} /> )} diff --git a/src/components/Shared/VideoGridList/VideoGridList.jsx b/src/components/Shared/VideoGridList/VideoGridList.jsx index e7b825818..2378599db 100644 --- a/src/components/Shared/VideoGridList/VideoGridList.jsx +++ b/src/components/Shared/VideoGridList/VideoGridList.jsx @@ -41,6 +41,7 @@ const VideoImageList = (props) => { onClick={() => { props.handleClick(tile); }} + data-testid={"assistant-media-grid-video-" + index} > {tile} diff --git a/tests/e2e/assistant.spec.js b/tests/e2e/assistant.spec.js index 3af42e43f..60fb733f6 100644 --- a/tests/e2e/assistant.spec.js +++ b/tests/e2e/assistant.spec.js @@ -1,19 +1,208 @@ import { test, expect } from './fixtures'; -// test('example test', async ({ page }) => { -// await page.goto('https://example.com'); -// await expect(page.locator('body')).toHaveText('Changed by my-extension'); -// }); - -test('Assstant page', async ({ page, extensionId }) => { - // Navigate to the assistant page - await page.goto(`chrome-extension://${extensionId}/popup.html#/app/assistant/`); - // Accept local storage usage - await page.getByText("Accept").click(); - await expect(page.getByTestId("url-media-results")).not.toBeVisible(); - await page.getByTestId("assistant-webpage-link").click(); - await page.locator("[data-testid='assistant-url-selected-input'] input").fill("https://www.youtube.com/watch?v=WaaL75G0qu0"); - await page.getByTestId("assistant-url-selected-analyse-btn").click(); - await expect(page.getByTestId("url-media-results")).toBeVisible(); - -}); + +const MediaType = { + video: "video", + image: "image" +}; + +const MediaVideoStatus = { + iframe: 0, + video: 1, + noEmbed: 2, +}; + +const MediaServices = { + analysisVideo: "navbar_analysis_video", + analysisImage: "navbar_analysis_image", + keyframes: "navbar_keyframes", + thumbnails: "navbar_thumbnails", + magnifier: "navbar_magnifier", + metadata: "navbar_metadata", + videoRights: "navbar_rights", + forensic: "navbar_forensic", + ocr: "navbar_ocr", + videoDownload: "assistant_video_download_action", + videoDownloadGeneric: "assistant_video_download_generic", + videoDownloadTiktok: "assistant_video_download_tiktok", +}; + +[ + // Twitter image post + { + url: "https://twitter.com/vesinfiltro/status/1253122594976468993/photo/1", + mediaType: MediaType.image, + services: [MediaServices.analysisImage, MediaServices.magnifier, MediaServices.forensic, MediaServices.ocr] + }, + // Twitter video post + { + url: "https://twitter.com/NatGeo/status/1334635273888514048/video/1", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.noEmbed, + services: [MediaServices.metadata, MediaServices.videoDownload] + }, + // Youtube video + { + url: "https://www.youtube.com/watch?v=UXrkN0iQmZQ", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.iframe, + services: [MediaServices.analysisVideo, MediaServices.keyframes, MediaServices.thumbnails, MediaServices.videoRights] + }, + // Youtube shorts + { + url: "https://www.youtube.com/shorts/RMGOds6SxF0", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.noEmbed, + services: [MediaServices.videoDownloadGeneric] + }, + // Facebook post with video + { + url: "https://www.facebook.com/natgeo/videos/10157990199633951", + videoGridIndex: 0, + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.video, + services: [MediaServices.analysisVideo, MediaServices.keyframes, MediaServices.videoDownload, MediaServices.videoDownloadGeneric] + }, + // Telegram post with video + { + url: "https://t.me/WeAreBREITBART/13745", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.video, + services: [MediaServices.metadata, MediaServices.videoDownload] + }, + // Telegram post with video - Fails, goes to the group page + // { + // url: "https://t.me/disclosetv/13970", + // mediaType: MediaType.video, + // mediaStatus: MediaVideoStatus.video, + // services: [MediaServices.metadata, MediaServices.videoDownload] + // }, + // Instagram post with an image + { + url: "https://www.instagram.com/p/CI2b-3usJoH/", + mediaType: MediaType.image, + services: [MediaServices.magnifier, MediaServices.forensic, MediaServices.ocr] + }, + // Instagram post with a video reel + { + url: "https://www.instagram.com/p/C8JwcyOiFDD/", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.noEmbed, + services: [MediaServices.videoDownloadGeneric] + }, + // TikTok video post + { + url: "https://www.tiktok.com/@deeptomcruise/video/7223086851236646149", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.noEmbed, + services: [MediaServices.videoDownloadTiktok] + }, + // VK link with images + { + url: "https://vk.com/wall-57424472_432185", + mediaType: MediaType.image, + imageGridIndex: 0, + services: [MediaServices.magnifier, MediaServices.metadata, MediaServices.forensic, MediaServices.ocr] + }, + // VK link with embedded video + { + url: "https://vk.com/video-221416054_456296074", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.noEmbed, + services: [MediaServices.videoDownloadGeneric] + }, + // Vimeo video post + { + url: "https://vimeo.com/389685467", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.iframe, + services: [MediaServices.videoDownloadGeneric] + }, + // Dailymotion video post + { + url: "https://www.dailymotion.com/video/x91gv4a", + mediaType: MediaType.video, + mediaStatus: MediaVideoStatus.iframe, + services: [MediaServices.videoDownloadGeneric] + }, + // Mastodon issues will be addressed very soon! + // // Mastodon link with youtube video link + // { + // url: "https://mstdn.social/@BBC/105203076554056414", + // mediaType: MediaType.video, + // mediaStatus: MediaVideoStatus.video, + // services: [] + // }, + // // Mastodon link with embedded video + // { + // url: "https://mstdn.social/@dtnsshow/112728823075224415", + // mediaType: MediaType.video, + // mediaStatus: MediaVideoStatus.video, + // services: [] + // }, +].forEach(({url, videoGridIndex, imageGridIndex, mediaType, mediaStatus, services}) => { + test(`Test assistant media services for url: ${url}`, async ({ page, extensionId }) => { + + // Navigate to the assistant page + await page.goto(`chrome-extension://${extensionId}/popup.html#/app/assistant/`); + // Accept local storage usage + await page.getByText("Accept").click(); + + // Component to display media should not be displayed at the start + await expect(page.getByTestId("url-media-results")).not.toBeVisible(); + + // Choose to enter url instead of uploading a file + await page.getByTestId("assistant-webpage-link").click(); + await page.locator("[data-testid='assistant-url-selected-input'] input").fill(url); + await page.getByTestId("assistant-url-selected-analyse-btn").click(); + + // Expecting a media post with images or video + await expect(page.getByTestId("url-media-results")).toBeVisible(); + + // If multiple images/videos exist, click on the media grid first + if(Number.isInteger(videoGridIndex)) + await page.getByTestId("assistant-media-grid-video-"+videoGridIndex).click(); + + if(Number.isInteger(imageGridIndex)) + await page.getByTestId("assistant-media-grid-image-"+imageGridIndex).click(); + + // If expecting an image, check that the image is shown + if(mediaType === MediaType.image){ + await expect(page.getByTestId("assistant-media-image")).toBeVisible(); + } + + if(mediaType === MediaType.video){ + await expect(page.getByTestId("assistant-media-video-container")).toBeVisible(); + if(mediaStatus !== null && mediaStatus !== undefined){ + switch (mediaStatus) { + case MediaVideoStatus.iframe: + await expect(page.getByTestId("assistant-media-video-iframe")).toBeVisible(); + break; + case MediaVideoStatus.video: + await expect(page.getByTestId("assistant-media-video-tag")).toBeVisible(); + break; + case MediaVideoStatus.noEmbed: + await expect(page.getByTestId("assistant-media-video-noembed")).toBeVisible(); + break; + + } + } + } + + // Checks that expected services are shown + for( const serviceId of services){ + await expect(page.getByTestId(serviceId)).toBeVisible(); + } + + // Ensure disabled services are not showing + for( const serviceKey in MediaServices){ + const serviceId = MediaServices[serviceKey]; + if(!services.includes(serviceId)) + await expect(page.getByTestId(serviceId)).not.toBeVisible(); + } + + }); + } +); + +