From 5aa87994c14974334a0ca1d9ca184a96e5e46625 Mon Sep 17 00:00:00 2001 From: Hfreitas Date: Fri, 3 Jul 2020 14:37:16 -0300 Subject: [PATCH 1/8] criado endpoint para busca de videos relacionados --- src/api/service.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/api/service.js b/src/api/service.js index ea02130..05b9229 100644 --- a/src/api/service.js +++ b/src/api/service.js @@ -1,8 +1,9 @@ const YOUTUBE_API_URL = 'https://www.googleapis.com/youtube/v3'; const YOUTUBE_AUTH_KEY = () => { - const auth = process.env.NODE_ENV === 'development' - ? process.env.REACT_APP_API_KEY - : null; + const auth = + process.env.NODE_ENV === 'development' + ? process.env.REACT_APP_API_KEY + : null; return auth; }; @@ -43,3 +44,16 @@ export const getVideoComments = async (videoId) => { return error; } }; + +export const getRelatedVideos = async (videoId) => { + const urlParams = `part=snippet&relatedToVideoId=${videoId}&type=video&key=${YOUTUBE_AUTH_KEY()}`; + const URL = `${YOUTUBE_API_URL}/search?${urlParams}`; + + try { + const response = await fetch(URL); + const data = await response.json(); + return data; + } catch (error) { + return error; + } +}; From 3d8399d2934653e545554c25bb7fe182c25085c3 Mon Sep 17 00:00:00 2001 From: Hfreitas Date: Fri, 3 Jul 2020 14:53:23 -0300 Subject: [PATCH 2/8] implementando videos laterais --- src/components/content/VideoPage/VideoPage.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/content/VideoPage/VideoPage.js b/src/components/content/VideoPage/VideoPage.js index 6c6b2df..33bf932 100644 --- a/src/components/content/VideoPage/VideoPage.js +++ b/src/components/content/VideoPage/VideoPage.js @@ -5,7 +5,7 @@ import VideoPlayerDescription from './VideoPlayer/VideoPlayerDescription'; import VideoPlayerInfo from './VideoPlayer/VideoPlayerInfo'; import VideoPlayerComments from './VideoPlayerComments/VideoPlayerComments'; import VideoSideBar from './VideoSideBar/VideoSideBar'; -import { getVideoInfo, getVideoComments } from '../../../api/service'; +import { getVideoInfo, getVideoComments, getRelatedVideos } from '../../../api/service'; class VideoPage extends Component { constructor(props) { @@ -18,7 +18,7 @@ class VideoPage extends Component { this.state = { videoId, - relatedVideos: data, + relatedVideos: [], shouldRedirect: false, videoInfo: null, videoComments: null, @@ -35,6 +35,11 @@ class VideoPage extends Component { this.setState({ videoId }, () => { getVideoInfo(videoId).then((data) => this.setState({ videoInfo: data.items[0] })); getVideoComments(videoId).then((data) => this.setState({ videoComments: data.items })); + getRelatedVideos(videoId).then((data) => this.setState({ + relatedVideos: data + .items + .slice(0, 24), + })); }); return this.setState({ shouldRedirect: true }); @@ -42,9 +47,14 @@ class VideoPage extends Component { mountVideoPage() { const { videoId } = this.state; - this.setState({ shouldRedirect: false }); getVideoInfo(videoId).then((data) => this.setState({ videoInfo: data.items[0] })); getVideoComments(videoId).then((data) => this.setState({ videoComments: data.items })); + getRelatedVideos(videoId).then((data) => this.setState({ + relatedVideos: data + .items + .slice(0, 24), + })); + this.setState({ shouldRedirect: false }); } renderVideoPage(videoId, videoInfo, videoComments, relatedVideos) { From b0e02130e213f8a51753ae532aae7a61756b3c38 Mon Sep 17 00:00:00 2001 From: Hfreitas Date: Fri, 3 Jul 2020 16:07:26 -0300 Subject: [PATCH 3/8] Task implementar sidebar dinamica --- src/__tests__/AltHeader.test.js | 50 ++++++++++++ src/__tests__/AltVideoPage.test.js | 76 +++++++++++++++++++ src/api/service.js | 7 +- src/components/content/SearchResult/index.js | 5 +- src/components/content/VideoPage/VideoPage.js | 5 +- 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 src/__tests__/AltHeader.test.js create mode 100644 src/__tests__/AltVideoPage.test.js diff --git a/src/__tests__/AltHeader.test.js b/src/__tests__/AltHeader.test.js new file mode 100644 index 0000000..ee7c0d5 --- /dev/null +++ b/src/__tests__/AltHeader.test.js @@ -0,0 +1,50 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import App from '../App'; +import mockSearchVideo from '../__mocks__/mockSearchVideo'; +import * as api from '../api/service'; + +jest.mock('react-router-dom', () => { + const moduloOriginal = jest.requireActual('react-router-dom'); + return { + ...moduloOriginal, + BrowserRouter: ({ children }) =>
{children}
, + }; +}); + +jest.mock('../api/service'); +api.searchVideos.mockImplementation(() => Promise.resolve(mockSearchVideo)); + +function renderWithRouter(ui, routeConfigs = {}) { + const route = routeConfigs.route || '/'; + const history = routeConfigs.history || createMemoryHistory({ initialEntries: [route] }); + return { + ...render({ui}), + history, + }; +} + +describe('Funcionalidades Componente Header', () => { + it('Renderiza apenas um link na tela', () => { + const { container } = renderWithRouter(); + const links = container.querySelectorAll('a'); + expect(links.length).toBe(2); + expect(links[1].href).toMatch('/results'); + }); + + it('Ao fazer uma busca redireciona a página de resultados', async () => { + const { getAllByRole, getByPlaceholderText, history } = renderWithRouter( + , + ); + expect(history.location.pathname).toBe('/'); + + const searchText = 'bugs'; + fireEvent.change(getByPlaceholderText(/search/i), { target: { value: searchText } }); + fireEvent.click(getAllByRole('link')[1]); + + await waitFor(() => expect(api.searchVideos).toHaveBeenCalled()); + expect(history.location.pathname).toBe(`/results/${searchText}`); + }); +}); diff --git a/src/__tests__/AltVideoPage.test.js b/src/__tests__/AltVideoPage.test.js new file mode 100644 index 0000000..e2a6a7b --- /dev/null +++ b/src/__tests__/AltVideoPage.test.js @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import VideoPage from '../components/content/VideoPage/VideoPage'; +import mockSearchVideo from '../__mocks__/mockSearchVideo'; +import mockGetVideoInfo from '../__mocks__/mockGetVideoInfo'; +import mockGetVideoComments from '../__mocks__/mockGetVideoComments'; +import * as api from '../api/service'; + +jest.mock('react-router-dom', () => { + const moduloOriginal = jest.requireActual('react-router-dom'); + return { + ...moduloOriginal, + BrowserRouter: ({ children }) =>
{children}
, + useHistory: () => ({ push: jest.fn() }), + }; +}); + +jest.mock('../api/service'); +api.getVideoInfo.mockImplementation(() => Promise.resolve(mockGetVideoInfo)); +api.getVideoComments.mockImplementation(() => Promise.resolve(mockGetVideoComments)); +api.getRelatedVideos.mockImplementation(() => Promise.resolve(mockSearchVideo)); + +function renderWithRouter(ui, routeConfigs = {}) { + const route = routeConfigs.route || '/'; + const history = routeConfigs.history || createMemoryHistory({ initialEntries: [route] }); + return { + ...render({ui}), + history, + }; +} + +describe('Funcionalidades Componente Video Page', () => { + it('Renderiza dados no vídeo na página', async () => { + const randomVideoID = mockSearchVideo.items[1].id.videoId; + + renderWithRouter( + , + ); + + await waitFor(() => expect(api.getVideoInfo).toHaveBeenCalled()); + await waitFor(() => expect(api.getVideoComments).toHaveBeenCalled()); + + expect(screen.getByTestId('videoplayer')).toBeInTheDocument(); + expect(screen.getByTestId('videoinfo')).toBeInTheDocument(); + expect(screen.getByTestId('channelinfo')).toBeInTheDocument(); + expect(screen.getByTestId('comments')).toBeInTheDocument(); + }); + + it('Vídeo selecionado atualiza os dados do vídeo atual na página', async () => { + const randomVideoID = mockSearchVideo.items[1].id.videoId; + const { history } = renderWithRouter( + , + { route: `/watch/${randomVideoID}` }, + ); + + await waitFor(() => expect(api.getVideoInfo).toHaveBeenCalled()); + await waitFor(() => expect(api.getVideoComments).toHaveBeenCalled()); + await waitFor(() => expect(api.getRelatedVideos).toHaveBeenCalled()); + expect(history.location.pathname).toBe(`/watch/${randomVideoID}`); + + fireEvent.click(screen.getAllByTestId('selectedVideo')[2]); + await waitFor(() => expect(api.getVideoInfo).toHaveBeenCalled()); + await waitFor(() => expect(api.getVideoComments).toHaveBeenCalled()); + await waitFor(() => expect(api.getRelatedVideos).toHaveBeenCalled()); + + expect(history.location.pathname).not.toEqual(`/watch/${randomVideoID}`); + }); +}); diff --git a/src/api/service.js b/src/api/service.js index 05b9229..a580372 100644 --- a/src/api/service.js +++ b/src/api/service.js @@ -1,9 +1,8 @@ const YOUTUBE_API_URL = 'https://www.googleapis.com/youtube/v3'; const YOUTUBE_AUTH_KEY = () => { - const auth = - process.env.NODE_ENV === 'development' - ? process.env.REACT_APP_API_KEY - : null; + const auth = process.env.NODE_ENV === 'development' + ? process.env.REACT_APP_API_KEY + : null; return auth; }; diff --git a/src/components/content/SearchResult/index.js b/src/components/content/SearchResult/index.js index 5201187..2c3add8 100644 --- a/src/components/content/SearchResult/index.js +++ b/src/components/content/SearchResult/index.js @@ -46,10 +46,7 @@ class SearchResult extends Component { diff --git a/src/components/content/VideoPage/VideoPage.js b/src/components/content/VideoPage/VideoPage.js index 33bf932..5958227 100644 --- a/src/components/content/VideoPage/VideoPage.js +++ b/src/components/content/VideoPage/VideoPage.js @@ -11,10 +11,7 @@ class VideoPage extends Component { constructor(props) { super(props); - const { - match: { params: { videoId } }, - location: { state: { data } }, - } = this.props; + const { match: { params: { videoId } } } = this.props; this.state = { videoId, From 35a77ff60f93821561d5a8ea4f22b2780a9be580 Mon Sep 17 00:00:00 2001 From: Hfreitas Date: Fri, 3 Jul 2020 16:45:54 -0300 Subject: [PATCH 4/8] =?UTF-8?q?implementa=C3=A7=C3=A3o=20de=20testes=20de?= =?UTF-8?q?=20novas=20funcionalidades=20e=20iniciando=20pagina=20de=20vide?= =?UTF-8?q?os=20assistidos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/AltHeader.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/AltHeader.test.js b/src/__tests__/AltHeader.test.js index ee7c0d5..b18ddf6 100644 --- a/src/__tests__/AltHeader.test.js +++ b/src/__tests__/AltHeader.test.js @@ -27,7 +27,7 @@ function renderWithRouter(ui, routeConfigs = {}) { } describe('Funcionalidades Componente Header', () => { - it('Renderiza apenas um link na tela', () => { + it('Renderiza dois links na tela', () => { const { container } = renderWithRouter(); const links = container.querySelectorAll('a'); expect(links.length).toBe(2); From f117d864ebb1bd8adcc76e7493a22632ff530f86 Mon Sep 17 00:00:00 2001 From: Hfreitas Date: Fri, 3 Jul 2020 17:39:21 -0300 Subject: [PATCH 5/8] atualizando --- src/__tests__/AltSearchResults.test.js | 65 +++++++++++++++++++ src/__tests__/SearchResult.test.js | 18 ++--- src/components/ViewedVideos.js | 13 ++++ .../SearchResult/VideoCard/VideoCard.js | 9 ++- src/components/content/SearchResult/index.js | 2 +- src/components/content/VideoPage/VideoPage.js | 20 +++++- .../VideoPage/VideoPlayer/VideoPlayer.js | 2 +- 7 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 src/__tests__/AltSearchResults.test.js create mode 100644 src/components/ViewedVideos.js diff --git a/src/__tests__/AltSearchResults.test.js b/src/__tests__/AltSearchResults.test.js new file mode 100644 index 0000000..27a842a --- /dev/null +++ b/src/__tests__/AltSearchResults.test.js @@ -0,0 +1,65 @@ +import React from 'react'; +import { + render, + screen, + fireEvent, + getByRole, + waitFor, +} from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import SearchResult from '../components/content/SearchResult/index'; +import App from '../App'; +import mockSearchVideo from '../__mocks__/mockSearchVideo'; +import mockGetVideoInfo from '../__mocks__/mockGetVideoInfo'; +import mockGetVideoComments from '../__mocks__/mockGetVideoComments'; +import * as api from '../api/service'; + +jest.mock('react-router-dom', () => { + const moduloOriginal = jest.requireActual('react-router-dom'); + return { + ...moduloOriginal, + BrowserRouter: ({ children }) =>
{children}
, + }; +}); + +jest.mock('../api/service'); +api.searchVideos.mockImplementation(() => Promise.resolve(mockSearchVideo)); +api.getVideoInfo.mockImplementation(() => Promise.resolve(mockGetVideoInfo)); +api.getVideoComments.mockImplementation(() => Promise.resolve(mockGetVideoComments)); +api.getRelatedVideos.mockImplementation(() => Promise.resolve(mockSearchVideo)); + +function renderWithRouter(ui, routeConfigs = {}) { + const route = routeConfigs.route || '/'; + const history = routeConfigs.history || createMemoryHistory({ initialEntries: [route] }); + return { + ...render({ui}), + history, + }; +} + +describe('Funcionalidades Componente Search Result', () => { + it('Renderiza uma lista de videos em cima da busca', async () => { + renderWithRouter( + , + ); + await waitFor(() => expect(api.searchVideos).toHaveBeenCalled()); + expect(screen.getAllByRole('link').length).toBeLessThan( + mockSearchVideo.items.length, + ); + }); + + it('Ao clicar em um video redireciona a pagina de display', async () => { + const { history } = renderWithRouter(, { route: '/results/bugs' }); + await waitFor(() => expect(api.searchVideos).toHaveBeenCalled()); + + const videoLink = screen.getAllByRole('link')[2]; + fireEvent.click(videoLink); + expect(history.location.pathname).toMatch(/watch/i); + + await waitFor(() => expect(api.getVideoInfo).toHaveBeenCalled()); + await waitFor(() => expect(api.getVideoComments).toHaveBeenCalled()); + + expect(screen.getByTestId('videoplayer')).toBeInTheDocument(); + }); +}); diff --git a/src/__tests__/SearchResult.test.js b/src/__tests__/SearchResult.test.js index 3ee1ff0..07898d2 100644 --- a/src/__tests__/SearchResult.test.js +++ b/src/__tests__/SearchResult.test.js @@ -7,7 +7,7 @@ import App from '../App'; import mockSearchVideo from '../__mocks__/mockSearchVideo'; import mockGetVideoInfo from '../__mocks__/mockGetVideoInfo'; import mockGetVideoComments from '../__mocks__/mockGetVideoComments'; -import * as api from '../api/service' +import * as api from '../api/service'; jest.mock('react-router-dom', () => { const moduloOriginal = jest.requireActual('react-router-dom'); @@ -15,17 +15,17 @@ jest.mock('react-router-dom', () => { ...moduloOriginal, BrowserRouter: ({ children }) => (
{children}
), }; -}) +}); jest.mock('../api/service'); api.searchVideos.mockImplementation( - () => Promise.resolve(mockSearchVideo) + () => Promise.resolve(mockSearchVideo), ); api.getVideoInfo.mockImplementation( - () => Promise.resolve(mockGetVideoInfo) + () => Promise.resolve(mockGetVideoInfo), ); api.getVideoComments.mockImplementation( - () => Promise.resolve(mockGetVideoComments) + () => Promise.resolve(mockGetVideoComments), ); function renderWithRouter(ui, routeConfigs = {}) { @@ -42,7 +42,7 @@ describe('Funcionalidades Componente Search Result', () => { renderWithRouter(); await waitFor(() => expect(api.searchVideos).toHaveBeenCalled()); expect(screen.getAllByRole('link').length).toBeLessThan(mockSearchVideo.items.length); - }) + }); it('Ao clicar em um video redireciona a pagina de display', async () => { const { history } = renderWithRouter(, { route: '/results/bugs' }); @@ -54,7 +54,7 @@ describe('Funcionalidades Componente Search Result', () => { await waitFor(() => expect(api.getVideoInfo).toHaveBeenCalled()); await waitFor(() => expect(api.getVideoComments).toHaveBeenCalled()); - + expect(screen.getByTestId('videoplayer')).toBeInTheDocument(); - }) -}) \ No newline at end of file + }); +}); diff --git a/src/components/ViewedVideos.js b/src/components/ViewedVideos.js new file mode 100644 index 0000000..8de8495 --- /dev/null +++ b/src/components/ViewedVideos.js @@ -0,0 +1,13 @@ +import React, { Component } from 'react'; + +class ViewedVideos extends Component { + constructor(props) { + super(props); + this + } + render() { + return
; + } +} + +export default ViewedVideos; diff --git a/src/components/content/SearchResult/VideoCard/VideoCard.js b/src/components/content/SearchResult/VideoCard/VideoCard.js index 7cb82ef..a3efb85 100644 --- a/src/components/content/SearchResult/VideoCard/VideoCard.js +++ b/src/components/content/SearchResult/VideoCard/VideoCard.js @@ -7,8 +7,15 @@ class VideoCard extends Component { return !!(props === param1 || props === param2); } - render() { + constructor(props) { + super(props); const { video } = this.props; + + this.state = { video }; + } + + render() { + const { video } = this.state; const { id: { kind }, snippet } = video; const { thumbnails: { medium: { url } }, channelTitle, description, title } = snippet; return ( diff --git a/src/components/content/SearchResult/index.js b/src/components/content/SearchResult/index.js index 2c3add8..90ef9fa 100644 --- a/src/components/content/SearchResult/index.js +++ b/src/components/content/SearchResult/index.js @@ -46,7 +46,7 @@ class SearchResult extends Component { diff --git a/src/components/content/VideoPage/VideoPage.js b/src/components/content/VideoPage/VideoPage.js index 5958227..1cb9bc3 100644 --- a/src/components/content/VideoPage/VideoPage.js +++ b/src/components/content/VideoPage/VideoPage.js @@ -5,7 +5,11 @@ import VideoPlayerDescription from './VideoPlayer/VideoPlayerDescription'; import VideoPlayerInfo from './VideoPlayer/VideoPlayerInfo'; import VideoPlayerComments from './VideoPlayerComments/VideoPlayerComments'; import VideoSideBar from './VideoSideBar/VideoSideBar'; -import { getVideoInfo, getVideoComments, getRelatedVideos } from '../../../api/service'; +import { + getVideoInfo, + getVideoComments, + getRelatedVideos, +} from '../../../api/service'; class VideoPage extends Component { constructor(props) { @@ -25,6 +29,7 @@ class VideoPage extends Component { } componentDidMount() { + this.saveViewedVideos(); this.mountVideoPage(); } @@ -54,6 +59,19 @@ class VideoPage extends Component { this.setState({ shouldRedirect: false }); } + saveViewedVideos() { + const { location: { state: { item } } } = this.props; + const viewedVideos = JSON.parse( + localStorage.getItem('viewedVideos') || '[]', + ); + const viewedArray = viewedVideos.filter((element) => element !== item); + const storeviewedVideo = localStorage.setItem( + 'viewedVideos', + JSON.stringify([...viewedArray, item]), + ); + return storeviewedVideo; + } + renderVideoPage(videoId, videoInfo, videoComments, relatedVideos) { return (
diff --git a/src/components/content/VideoPage/VideoPlayer/VideoPlayer.js b/src/components/content/VideoPage/VideoPlayer/VideoPlayer.js index b90e78b..ff2d81c 100644 --- a/src/components/content/VideoPage/VideoPlayer/VideoPlayer.js +++ b/src/components/content/VideoPage/VideoPlayer/VideoPlayer.js @@ -5,7 +5,7 @@ import '../../../../css/chanelInfo.css'; class VideoPlayer extends Component { render() { const { embedId, title } = this.props; - const playerURL = `https://www.youtube.com/embed/${embedId}`; + const playerURL = `https://www.youtube.com/embed/${embedId}?autoplay=1`; return (