Merge branch 'tagsclickable' into 'master'

Tagsclickable

See merge request lukas/openmediacenter!5
This commit is contained in:
Lukas Heiligenbrunner 2020-06-24 20:29:04 +00:00
commit afae31618c
14 changed files with 155 additions and 142 deletions

View File

@ -14,29 +14,38 @@ class App extends React.Component {
this.state = {page: "default"}; this.state = {page: "default"};
// bind this to the method for being able to call methods such as this.setstate // bind this to the method for being able to call methods such as this.setstate
this.showVideo = this.showVideo.bind(this); this.changeRootElement = this.changeRootElement.bind(this);
this.hideVideo = this.hideVideo.bind(this); this.returnToLastElement = this.returnToLastElement.bind(this);
} }
videoelement = null; newElement = null;
MainBody() { MainBody() {
let page; let page;
if (this.state.page === "default") { if (this.state.page === "default") {
page = <HomePage viewbinding={{showVideo: this.showVideo, hideVideo: this.hideVideo}}/>; page = <HomePage viewbinding={{
changeRootElement: this.changeRootElement,
returnToLastElement: this.returnToLastElement
}}/>;
this.mypage = page; this.mypage = page;
} else if (this.state.page === "random") { } else if (this.state.page === "random") {
page = <RandomPage viewbinding={{showVideo: this.showVideo, hideVideo: this.hideVideo}}/>; page = <RandomPage viewbinding={{
changeRootElement: this.changeRootElement,
returnToLastElement: this.returnToLastElement
}}/>;
this.mypage = page; this.mypage = page;
} else if (this.state.page === "settings") { } else if (this.state.page === "settings") {
page = <SettingsPage/>; page = <SettingsPage/>;
this.mypage = page; this.mypage = page;
} else if (this.state.page === "categories") { } else if (this.state.page === "categories") {
page = <CategoryPage viewbinding={{showVideo: this.showVideo, hideVideo: this.hideVideo}}/>; page = <CategoryPage viewbinding={{
changeRootElement: this.changeRootElement,
returnToLastElement: this.returnToLastElement
}}/>;
this.mypage = page; this.mypage = page;
} else if (this.state.page === "video") { } else if (this.state.page === "video") {
// show videoelement if neccessary // show videoelement if neccessary
page = this.videoelement; page = this.newElement;
console.log(page); console.log(page);
} else if (this.state.page === "lastpage") { } else if (this.state.page === "lastpage") {
@ -86,19 +95,18 @@ class App extends React.Component {
); );
} }
showVideo(element) { changeRootElement(element) {
this.videoelement = element; this.newElement = element;
this.setState({ this.setState({
page: "video" page: "video"
}); });
} }
hideVideo() { returnToLastElement() {
this.setState({ this.setState({
page: "lastpage" page: "lastpage"
}); });
this.element = null;
} }
} }

View File

@ -21,7 +21,7 @@ describe('<App/>', function () {
it('simulate video view change ', function () { it('simulate video view change ', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.instance().showVideo(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'></div>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
}); });
@ -29,11 +29,11 @@ describe('<App/>', function () {
it('test hide video again', function () { it('test hide video again', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.instance().showVideo(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'></div>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
wrapper.instance().hideVideo(); wrapper.instance().returnToLastElement();
expect(wrapper.find("HomePage")).toHaveLength(1); expect(wrapper.find("HomePage")).toHaveLength(1);
}); });
@ -43,11 +43,11 @@ describe('<App/>', function () {
wrapper.find(".nav-link").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click"); wrapper.find(".nav-link").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click");
wrapper.instance().showVideo(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'></div>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
wrapper.instance().hideVideo(); wrapper.instance().returnToLastElement();
expect(wrapper.find("RandomPage")).toHaveLength(1); expect(wrapper.find("RandomPage")).toHaveLength(1);
}); });

View File

@ -1,13 +1,11 @@
import React from "react"; import React from "react";
import "./Preview.css"; import "./Preview.css";
import Player from "../../pages/Player/Player"; import Player from "../../pages/Player/Player";
import VideoContainer from "../VideoContainer/VideoContainer";
import {Spinner} from "react-bootstrap"; import {Spinner} from "react-bootstrap";
class Preview extends React.Component { class Preview extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.props = props;
this.state = { this.state = {
previewpicture: null, previewpicture: null,
@ -60,44 +58,14 @@ class Preview extends React.Component {
itemClick() { itemClick() {
console.log("item clicked!" + this.state.name); console.log("item clicked!" + this.state.name);
this.props.viewbinding.showVideo(<Player this.props.viewbinding.changeRootElement(
<Player
viewbinding={this.props.viewbinding} viewbinding={this.props.viewbinding}
movie_id={this.props.movie_id}/>); movie_id={this.props.movie_id}/>);
} }
} }
export class TagPreview extends React.Component { export class TagPreview extends React.Component {
constructor(props, context) {
super(props, context);
this.props = props;
}
fetchVideoData(tag) {
console.log(tag);
const updateRequest = new FormData();
updateRequest.append('action', 'getMovies');
updateRequest.append('tag', tag);
console.log("fetching data");
// fetch all videos available
fetch('/api/videoload.php', {method: 'POST', body: updateRequest})
.then((response) => response.json()
.then((result) => {
console.log(result);
this.props.categorybinding(
<VideoContainer
data={result}
viewbinding={this.props.viewbinding}/>, tag
);
}))
.catch(() => {
console.log("no connection to backend");
});
}
render() { render() {
return ( return (
<div className='videopreview tagpreview' onClick={() => this.itemClick()}> <div className='videopreview tagpreview' onClick={() => this.itemClick()}>
@ -109,7 +77,7 @@ export class TagPreview extends React.Component {
} }
itemClick() { itemClick() {
this.fetchVideoData(this.props.name); this.props.categorybinding(this.props.name);
} }
} }

View File

@ -22,7 +22,7 @@ describe('<Preview/>', function () {
const wrapper = shallow(<Preview/>); const wrapper = shallow(<Preview/>);
wrapper.setProps({ wrapper.setProps({
viewbinding: { viewbinding: {
showVideo: () => { changeRootElement: () => {
func() func()
} }
} }
@ -78,14 +78,7 @@ describe('<TagPreview/>', function () {
}); });
it('click event triggered', done => { it('click event triggered', function () {
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const func = jest.fn(); const func = jest.fn();
const wrapper = shallow(<TagPreview/>); const wrapper = shallow(<TagPreview/>);
@ -96,19 +89,11 @@ describe('<TagPreview/>', function () {
}); });
// first call of fetch is getting of available tags // first call of fetch is getting of available tags
expect(global.fetch).toHaveBeenCalledTimes(0); expect(func).toHaveBeenCalledTimes(0);
wrapper.find('.videopreview').simulate('click'); wrapper.find('.videopreview').simulate('click');
// now called 1 times // now called 1 times
expect(global.fetch).toHaveBeenCalledTimes(1);
process.nextTick(() => {
//callback to close window should have called
expect(func).toHaveBeenCalledTimes(1); expect(func).toHaveBeenCalledTimes(1);
global.fetch.mockClear();
done();
});
}); });
}); });

View File

@ -1,21 +1,24 @@
import React from "react"; import React from "react";
import "./Tag.css" import "./Tag.css"
import CategoryPage from "../../pages/CategoryPage/CategoryPage";
class Tag extends React.Component { class Tag extends React.Component {
constructor(props, context) {
super(props, context);
this.props = props;
}
render() { render() {
// todo onclick events correctlyy
return ( return (
<button className='tagbtn' onClick={this.props.onClick} <button className='tagbtn' onClick={() => this.TagClick()}
data-testid="Test-Tag">{this.props.children}</button> data-testid="Test-Tag">{this.props.children}</button>
); );
} }
TagClick() {
const tag = this.props.children.toString().toLowerCase();
this.props.viewbinding.changeRootElement(
<CategoryPage
category={tag}
viewbinding={this.props.viewbinding}/>);
}
} }
export default Tag; export default Tag;

View File

@ -5,6 +5,14 @@ import "@testing-library/jest-dom"
import {shallow} from 'enzyme' import {shallow} from 'enzyme'
describe('<Tag/>', function () { describe('<Tag/>', function () {
function prepareFetchApi(response) {
const mockJsonPromise = Promise.resolve(response);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
return (jest.fn().mockImplementation(() => mockFetchPromise));
}
it('renders without crashing ', function () { it('renders without crashing ', function () {
const wrapper = shallow(<Tag>test</Tag>); const wrapper = shallow(<Tag>test</Tag>);
wrapper.unmount(); wrapper.unmount();
@ -14,4 +22,21 @@ describe('<Tag/>', function () {
const wrapper = shallow(<Tag>test</Tag>); const wrapper = shallow(<Tag>test</Tag>);
expect(wrapper.children().text()).toBe("test"); expect(wrapper.children().text()).toBe("test");
}); });
it('click event triggered and setvideo callback called', function () {
global.fetch = prepareFetchApi({});
const func = jest.fn();
const elem = {
changeRootElement: () => func()
};
const wrapper = shallow(<Tag
viewbinding={elem}>test</Tag>);
expect(func).toBeCalledTimes(0);
wrapper.simulate("click");
expect(func).toBeCalledTimes(1);
});
}); });

View File

@ -5,13 +5,12 @@ import Tag from "../../elements/Tag/Tag";
import {TagPreview} from "../../elements/Preview/Preview"; import {TagPreview} from "../../elements/Preview/Preview";
import NewTagPopup from "../../elements/NewTagPopup/NewTagPopup"; import NewTagPopup from "../../elements/NewTagPopup/NewTagPopup";
import PageTitle from "../../elements/PageTitle/PageTitle"; import PageTitle from "../../elements/PageTitle/PageTitle";
import VideoContainer from "../../elements/VideoContainer/VideoContainer";
class CategoryPage extends React.Component { class CategoryPage extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.props = props;
this.state = { this.state = {
loadedtags: [], loadedtags: [],
selected: null selected: null
@ -19,8 +18,13 @@ class CategoryPage extends React.Component {
} }
componentDidMount() { componentDidMount() {
// check if predefined category is set
if (this.props.category) {
this.fetchVideoData(this.props.category);
} else {
this.loadTags(); this.loadTags();
} }
}
render() { render() {
return ( return (
@ -31,10 +35,26 @@ class CategoryPage extends React.Component {
<SideBar> <SideBar>
<div className='sidebartitle'>Default Tags:</div> <div className='sidebartitle'>Default Tags:</div>
<Tag>All</Tag> <Tag viewbinding={{
<Tag>FullHd</Tag> changeRootElement: (e) => {
<Tag>LowQuality</Tag> this.loadTag(e.props.category)
<Tag>HD</Tag> }
}}>All</Tag>
<Tag viewbinding={{
changeRootElement: (e) => {
this.loadTag(e.props.category)
}
}}>FullHd</Tag>
<Tag viewbinding={{
changeRootElement: (e) => {
this.loadTag(e.props.category)
}
}}>LowQuality</Tag>
<Tag viewbinding={{
changeRootElement: (e) => {
this.loadTag(e.props.category)
}
}}>HD</Tag>
<hr/> <hr/>
<button data-testid='btnaddtag' className='btn btn-success' onClick={() => { <button data-testid='btnaddtag' className='btn btn-success' onClick={() => {
this.setState({popupvisible: true}) this.setState({popupvisible: true})
@ -42,8 +62,17 @@ class CategoryPage extends React.Component {
</button> </button>
</SideBar> </SideBar>
{!this.state.selected ? {this.state.selected ?
(<div className='maincontent'> <>
{this.videodata ?
<VideoContainer
data={this.videodata}
viewbinding={this.props.viewbinding}/> : null}
<button data-testid='backbtn' className="btn btn-success"
onClick={this.loadCategoryPageDefault}>Back
</button>
</> :
<div className='maincontent'>
{this.state.loadedtags ? {this.state.loadedtags ?
this.state.loadedtags.map((m) => ( this.state.loadedtags.map((m) => (
<TagPreview <TagPreview
@ -51,16 +80,10 @@ class CategoryPage extends React.Component {
name={m.tag_name} name={m.tag_name}
tag_id={m.tag_id} tag_id={m.tag_id}
viewbinding={this.props.viewbinding} viewbinding={this.props.viewbinding}
categorybinding={this.setPage}/> categorybinding={this.loadTag}/>
)) : )) :
"loading"} "loading"}
</div>) : </div>
<>
{this.selectionelements}
<button data-testid='backbtn' className="btn btn-success"
onClick={this.loadCategoryPageDefault}>Back
</button>
</>
} }
{this.state.popupvisible ? {this.state.popupvisible ?
@ -76,14 +99,34 @@ class CategoryPage extends React.Component {
); );
} }
setPage = (element, tagname) => { loadTag = (tagname) => {
this.selectionelements = element; this.fetchVideoData(tagname);
this.setState({selected: tagname});
}; };
fetchVideoData(tag) {
console.log(tag);
const updateRequest = new FormData();
updateRequest.append('action', 'getMovies');
updateRequest.append('tag', tag);
console.log("fetching data");
// fetch all videos available
fetch('/api/videoload.php', {method: 'POST', body: updateRequest})
.then((response) => response.json()
.then((result) => {
this.videodata = result;
this.setState({selected: null}); // needed to trigger the state reload correctly
this.setState({selected: tag});
}))
.catch(() => {
console.log("no connection to backend");
});
}
loadCategoryPageDefault = () => { loadCategoryPageDefault = () => {
this.setState({selected: null}); this.setState({selected: null});
this.loadTags();
}; };
/** /**

View File

@ -86,8 +86,6 @@ describe('<CategoryPage/>', function () {
process.nextTick(() => { process.nextTick(() => {
// expect callback to have loaded correct tag // expect callback to have loaded correct tag
expect(wrapper.state().selected).toBe("testname"); expect(wrapper.state().selected).toBe("testname");
// expect to receive a videocontainer with simulated data
expect(wrapper.instance().selectionelements).toMatchObject(<VideoContainer data={[{}, {}]}/>);
global.fetch.mockClear(); global.fetch.mockClear();
done(); done();
@ -104,4 +102,13 @@ describe('<CategoryPage/>', function () {
wrapper.find('[data-testid="backbtn"]').simulate("click"); wrapper.find('[data-testid="backbtn"]').simulate("click");
expect(wrapper.state().selected).toBeNull(); expect(wrapper.state().selected).toBeNull();
}); });
it('load categorypage with predefined tag', function () {
const func = jest.fn();
CategoryPage.prototype.fetchVideoData = func;
const wrapper = shallow(<CategoryPage category="fullhd"/>);
expect(func).toBeCalledTimes(1);
});
}); });

View File

@ -146,26 +146,10 @@ class HomePage extends React.Component {
<div className='sidebarinfo'><b>{this.state.sideinfo.tagnr}</b> different Tags!</div> <div className='sidebarinfo'><b>{this.state.sideinfo.tagnr}</b> different Tags!</div>
<hr/> <hr/>
<div className='sidebartitle'>Default Tags:</div> <div className='sidebartitle'>Default Tags:</div>
<Tag onClick={() => { <Tag viewbinding={this.props.viewbinding}>All</Tag>
this.setState({tag: "All"}); <Tag viewbinding={this.props.viewbinding}>FullHd</Tag>
this.fetchVideoData("all"); <Tag viewbinding={this.props.viewbinding}>LowQuality</Tag>
}}>All <Tag viewbinding={this.props.viewbinding}>HD</Tag>
</Tag>
<Tag onClick={() => {
this.setState({tag: "Full HD"});
this.fetchVideoData("fullhd");
}}>FullHd
</Tag>
<Tag onClick={() => {
this.setState({tag: "Low Quality"});
this.fetchVideoData("lowquality");
}}>LowQuality
</Tag>
<Tag onClick={() => {
this.setState({tag: "HD"});
this.fetchVideoData("hd");
}}>HD
</Tag>
</SideBar> </SideBar>
{this.state.data.length !== 0 ? {this.state.data.length !== 0 ?
<VideoContainer <VideoContainer

View File

@ -22,18 +22,6 @@ describe('<HomePage/>', function () {
wrapper.unmount(); wrapper.unmount();
}); });
it('ckeck default tag click events', function () {
const wrapper = shallow(<HomePage/>);
global.fetch = prepareFetchApi({});
expect(global.fetch).toBeCalledTimes(0);
// click every tag button
wrapper.find("Tag").map((i) => {
i.simulate("click");
});
expect(global.fetch).toBeCalledTimes(4);
});
it('test data insertion', function () { it('test data insertion', function () {
const wrapper = shallow(<HomePage/>); const wrapper = shallow(<HomePage/>);

View File

@ -21,8 +21,6 @@ class Player extends React.Component {
tags: [], tags: [],
popupvisible: false popupvisible: false
}; };
this.props = props;
} }
options = { options = {
@ -66,7 +64,9 @@ class Player extends React.Component {
<hr/> <hr/>
<div className='sidebartitle'>Tags:</div> <div className='sidebartitle'>Tags:</div>
{this.state.tags.map((m) => ( {this.state.tags.map((m) => (
<Tag key={m.tag_name}>{m.tag_name}</Tag> <Tag
key={m.tag_name}
viewbinding={this.props.viewbinding}>{m.tag_name}</Tag>
))} ))}
</SideBar> </SideBar>
@ -149,7 +149,7 @@ class Player extends React.Component {
} }
closebtn() { closebtn() {
this.props.viewbinding.hideVideo(); this.props.viewbinding.returnToLastElement();
} }
} }

View File

@ -99,7 +99,7 @@ describe('<Player/>', function () {
wrapper.setProps({ wrapper.setProps({
viewbinding: { viewbinding: {
hideVideo: () => { returnToLastElement: () => {
func() func()
} }
} }

View File

@ -29,7 +29,9 @@ class RandomPage extends React.Component {
<SideBar> <SideBar>
<div className='sidebartitle'>Visible Tags:</div> <div className='sidebartitle'>Visible Tags:</div>
{this.state.tags.map((m) => ( {this.state.tags.map((m) => (
<Tag>{m.tag_name}</Tag> <Tag
key={m.tag_name}
viewbinding={this.props.viewbinding}>{m.tag_name}</Tag>
))} ))}
</SideBar> </SideBar>