diff --git a/src/App.js b/src/App.js
index 77dddb4..46930bd 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,7 +8,7 @@ import Header from './components/header/Header';
import VideoPage from './components/content/VideoPage/VideoPage';
import SearchResult from './components/content/SearchResult';
import NotFound from './components/content/NotFound';
-import InitialPage from './components/content/InitialPage';
+import Profile from './components/Profile';
class App extends Component {
render() {
@@ -28,6 +28,7 @@ class App extends Component {
path="/results/:searchParam"
render={(props) => }
/>
+
diff --git a/src/__tests__/AltHeader.test.js b/src/__tests__/AltHeader.test.js
new file mode 100644
index 0000000..43a0715
--- /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 dois links na tela', () => {
+ const { container } = renderWithRouter();
+ const links = container.querySelectorAll('a');
+ expect(links.length).toBe(3);
+ 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__/AltSearchResults.test.js b/src/__tests__/AltSearchResults.test.js
new file mode 100644
index 0000000..c0d0bea
--- /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')[3];
+ 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__/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/__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/api/localStorage.js b/src/api/localStorage.js
index 7b8e547..b4b8498 100644
--- a/src/api/localStorage.js
+++ b/src/api/localStorage.js
@@ -1,7 +1,8 @@
export default function addToLocalStorage(key, value) {
- const jsonKey = JSON.stringify(key);
- if (!localStorage[jsonKey]) localStorage[jsonKey] = JSON.stringify([]);
- const searchHistory = JSON.parse(localStorage[jsonKey]);
- const updatedSearchHistory = [...searchHistory, value];
- localStorage[jsonKey] = JSON.stringify(updatedSearchHistory);
+ // const jsonKey = JSON.stringify(key);
+ if (!localStorage[key]) localStorage[key] = JSON.stringify([]);
+ const searchHistory = JSON.parse(localStorage[key]);
+ const historyArray = searchHistory.filter((element) => element !== value);
+ const updatedSearchHistory = [...historyArray, value];
+ localStorage[key] = JSON.stringify(updatedSearchHistory);
}
diff --git a/src/api/service.js b/src/api/service.js
index ea02130..a580372 100644
--- a/src/api/service.js
+++ b/src/api/service.js
@@ -43,3 +43,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;
+ }
+};
diff --git a/src/components/Profile/ViewedVideos.js b/src/components/Profile/ViewedVideos.js
new file mode 100644
index 0000000..9faf3d5
--- /dev/null
+++ b/src/components/Profile/ViewedVideos.js
@@ -0,0 +1,41 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+import VideoCard from '../content/SearchResult/VideoCard/VideoCard';
+import '../../css/sideBar.css';
+
+class ViewedVideos extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = { data: [] };
+ }
+
+ componentDidMount() {
+ this.mountViewedComponent();
+ }
+
+ mountViewedComponent() {
+ const data = new Set(JSON.parse(localStorage.getItem('viewedVideos') || '[]'));
+ return this.setState({ data: [...data] });
+ }
+
+ render() {
+ const { data } = this.state;
+ if (!data) return Sem vídeos favoritos
;
+ return (
+
+ {data.map((item) => (
+
+
+
+ ))}
+
+ );
+ }
+}
+
+export default ViewedVideos;
diff --git a/src/components/Profile/index.js b/src/components/Profile/index.js
new file mode 100644
index 0000000..ee1bc25
--- /dev/null
+++ b/src/components/Profile/index.js
@@ -0,0 +1,23 @@
+import React, { Component } from 'react';
+import ViewedVideos from './ViewedVideos';
+
+class Profile extends Component {
+ render() {
+ return
+
+
Vídeos Pesquisados
+
+
+
+
+
+
Vídeos Favoritos
+
+
+
Assistir mais tarde
+
+ ;
+ }
+}
+
+export default Profile;
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 5201187..90ef9fa 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 6c6b2df..9a35ee1 100644
--- a/src/components/content/VideoPage/VideoPage.js
+++ b/src/components/content/VideoPage/VideoPage.js
@@ -5,20 +5,22 @@ 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';
+import addToLocalStorage from '../../../api/localStorage';
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,
- relatedVideos: data,
+ relatedVideos: [],
shouldRedirect: false,
videoInfo: null,
videoComments: null,
@@ -28,23 +30,35 @@ class VideoPage extends Component {
}
componentDidMount() {
+ const { location: { state: { item } } } = this.props;
+ addToLocalStorage('viewedVideos', item);
this.mountVideoPage();
}
- handleSelectedVideo(videoId) {
+ handleSelectedVideo(videoId, video) {
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),
+ }));
});
-
+ addToLocalStorage('viewedVideos', video);
return this.setState({ shouldRedirect: true });
}
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) {
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 (
);
}
diff --git a/src/components/header/SearchBar.js b/src/components/header/SearchBar.js
index d2544ed..e98d2c8 100644
--- a/src/components/header/SearchBar.js
+++ b/src/components/header/SearchBar.js
@@ -9,7 +9,7 @@ class SearchBar extends Component {
this.state = {
searchInput: '',
- keyEnter: false
+ keyEnter: false,
};
this.handleSearchInput = this.handleSearchInput.bind(this);
@@ -17,12 +17,10 @@ class SearchBar extends Component {
}
pressEnterKey(event) {
- const { searchInput } = this.state;
const enterOrSpace = event.key === 'Enter'
|| event.keyCode === 13;
if (enterOrSpace) {
- console.log('enter')
event.preventDefault();
this.setState({ keyEnter: true });
}
@@ -44,7 +42,8 @@ class SearchBar extends Component {
- )}
+ );
+ }
return (