Merge branch 'noredirectonhomepage' into 'master'

no redirect to categorypage - reindex after deb install - no multiply add of same tag possible

See merge request lukas/openmediacenter!21
This commit is contained in:
Lukas Heiligenbrunner 2020-10-25 18:48:23 +00:00
commit 83227da6da
43 changed files with 493 additions and 448 deletions

View File

@ -1,4 +1,4 @@
image: node:latest image: node:14
stages: stages:
- prepare - prepare
@ -21,7 +21,6 @@ prepare:
stage: prepare stage: prepare
script: script:
- npm install --progress=false - npm install --progress=false
- npm update --progress=false
build: build:
stage: build stage: build

View File

@ -212,6 +212,28 @@ class VideoParser {
} }
} }
/**
* get all videoinfos of a video file
*
* @param $video string name including extension
* @return object all infos as object
*/
private function _get_video_attributes(string $video) {
$command = "mediainfo \"../$this->videopath$video\" --Output=JSON";
$output = shell_exec($command);
return json_decode($output);
}
/**
* write a line to the output log file
*
* @param string $message message to write
*/
public function writeLog(string $message) {
file_put_contents("/tmp/output.log", $message, FILE_APPEND);
flush();
}
/** /**
* insert the corresponding videosize tag to a specific videoid * insert the corresponding videosize tag to a specific videoid
* @param $width int video width * @param $width int video width
@ -246,28 +268,6 @@ class VideoParser {
} }
} }
/**
* get all videoinfos of a video file
*
* @param $video string name including extension
* @return object all infos as object
*/
private function _get_video_attributes(string $video) {
$command = "mediainfo \"../$this->videopath$video\" --Output=JSON";
$output = shell_exec($command);
return json_decode($output);
}
/**
* write a line to the output log file
*
* @param string $message message to write
*/
public function writeLog(string $message) {
file_put_contents("/tmp/output.log", $message, FILE_APPEND);
flush();
}
/** /**
* ckecks if tag exists -- if not creates it * ckecks if tag exists -- if not creates it
* @param string $tagname the name of the tag * @param string $tagname the name of the tag

View File

@ -34,6 +34,11 @@ abstract class RequestBase {
} }
} }
/**
* add the action handlers in this abstract method
*/
abstract function initHandlers();
/** /**
* Send response message and exit script * Send response message and exit script
* @param $message string the response message * @param $message string the response message
@ -42,9 +47,4 @@ abstract class RequestBase {
echo $message; echo $message;
exit(0); exit(0);
} }
/**
* add the action handlers in this abstract method
*/
abstract function initHandlers();
} }

View File

@ -11,22 +11,6 @@ class Tags extends RequestBase {
$this->getFromDB(); $this->getFromDB();
} }
private function getFromDB(){
/**
* returns all available tags from database
*/
$this->addActionHandler("getAllTags", function () {
$query = "SELECT tag_name,tag_id from tags";
$result = $this->conn->query($query);
$rows = array();
while ($r = mysqli_fetch_assoc($result)) {
array_push($rows, $r);
}
$this->commitMessage(json_encode($rows));
});
}
private function addToDB() { private function addToDB() {
/** /**
* creates a new tag * creates a new tag
@ -34,7 +18,8 @@ class Tags extends RequestBase {
* * tagname -- name of the new tag * * tagname -- name of the new tag
*/ */
$this->addActionHandler("createTag", function () { $this->addActionHandler("createTag", function () {
$query = "INSERT INTO tags (tag_name) VALUES ('" . $_POST['tagname'] . "')"; // skip tag create if already existing
$query = "INSERT IGNORE INTO tags (tag_name) VALUES ('" . $_POST['tagname'] . "')";
if ($this->conn->query($query) === TRUE) { if ($this->conn->query($query) === TRUE) {
$this->commitMessage('{"result":"success"}'); $this->commitMessage('{"result":"success"}');
@ -54,7 +39,8 @@ class Tags extends RequestBase {
$movieid = $_POST['movieid']; $movieid = $_POST['movieid'];
$tagid = $_POST['id']; $tagid = $_POST['id'];
$query = "INSERT INTO video_tags(tag_id, video_id) VALUES ('$tagid','$movieid')"; // skip tag add if already assigned
$query = "INSERT IGNORE INTO video_tags(tag_id, video_id) VALUES ('$tagid','$movieid')";
if ($this->conn->query($query) === TRUE) { if ($this->conn->query($query) === TRUE) {
$this->commitMessage('{"result":"success"}'); $this->commitMessage('{"result":"success"}');
@ -63,4 +49,20 @@ class Tags extends RequestBase {
} }
}); });
} }
private function getFromDB() {
/**
* returns all available tags from database
*/
$this->addActionHandler("getAllTags", function () {
$query = "SELECT tag_name,tag_id from tags";
$result = $this->conn->query($query);
$rows = array();
while ($r = mysqli_fetch_assoc($result)) {
array_push($rows, $r);
}
$this->commitMessage(json_encode($rows));
});
}
} }

View File

@ -29,7 +29,8 @@ class Video extends RequestBase {
$query = "SELECT movie_id,movie_name FROM videos ORDER BY create_date DESC, movie_name"; $query = "SELECT movie_id,movie_name FROM videos ORDER BY create_date DESC, movie_name";
if (isset($_POST['tag'])) { if (isset($_POST['tag'])) {
$tag = $_POST['tag']; $tag = $_POST['tag'];
if ($_POST['tag'] != "all") { // if not all tags allowed filter for specific one
if (strtolower($_POST['tag']) != "all") {
$query = "SELECT movie_id,movie_name FROM videos $query = "SELECT movie_id,movie_name FROM videos
INNER JOIN video_tags vt on videos.movie_id = vt.video_id INNER JOIN video_tags vt on videos.movie_id = vt.video_id
INNER JOIN tags t on vt.tag_id = t.tag_id INNER JOIN tags t on vt.tag_id = t.tag_id

View File

@ -30,3 +30,6 @@ chown -R www-data:www-data /var/www/openmediacenter
# restart services # restart services
systemctl restart nginx systemctl restart nginx
# trigger a movie reindex
php /var/www/openmediacenter/extractvideopreviews.php

View File

@ -10,14 +10,14 @@
"bootstrap": "^4.5.3", "bootstrap": "^4.5.3",
"plyr-react": "^2.2.0", "plyr-react": "^2.2.0",
"react": "^16.14.0", "react": "^16.14.0",
"react-bootstrap": "^1.3.0", "react-bootstrap": "^1.4.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-scripts": "^3.4.3" "react-scripts": "^3.4.4"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test --reporters=jest-junit --reporters=default --ci --silent", "test": "react-scripts test --reporters=jest-junit --reporters=default",
"coverage": "react-scripts test --coverage --watchAll=false", "coverage": "react-scripts test --coverage --watchAll=false",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
@ -31,7 +31,7 @@
"text-summary" "text-summary"
] ]
}, },
"proxy": "http://192.168.0.42", "proxy": "http://192.168.0.42:8080",
"homepage": "/", "homepage": "/",
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import HomePage from "./pages/HomePage/HomePage"; import HomePage from './pages/HomePage/HomePage';
import RandomPage from "./pages/RandomPage/RandomPage"; import RandomPage from './pages/RandomPage/RandomPage';
import GlobalInfos from "./GlobalInfos"; import GlobalInfos from './GlobalInfos';
// include bootstraps css // include bootstraps css
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
import style from './App.module.css' import style from './App.module.css';
import SettingsPage from "./pages/SettingsPage/SettingsPage"; import SettingsPage from './pages/SettingsPage/SettingsPage';
import CategoryPage from "./pages/CategoryPage/CategoryPage"; import CategoryPage from './pages/CategoryPage/CategoryPage';
/** /**
* The main App handles the main tabs and which content to show * The main App handles the main tabs and which content to show
@ -19,10 +19,10 @@ class App extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = { this.state = {
page: "default", page: 'default',
generalSettingsLoaded: false, generalSettingsLoaded: false,
passwordsupport: null, passwordsupport: null,
mediacentername: "OpenMediaCenter" mediacentername: 'OpenMediaCenter'
}; };
// 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
@ -67,24 +67,24 @@ class App extends React.Component {
*/ */
MainBody() { MainBody() {
let page; let page;
if (this.state.page === "default") { if (this.state.page === 'default') {
page = <HomePage viewbinding={this.constructViewBinding()}/>; page = <HomePage viewbinding={this.constructViewBinding()}/>;
this.mypage = page; this.mypage = page;
} else if (this.state.page === "random") { } else if (this.state.page === 'random') {
page = <RandomPage viewbinding={this.constructViewBinding()}/>; page = <RandomPage viewbinding={this.constructViewBinding()}/>;
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={this.constructViewBinding()}/>; page = <CategoryPage viewbinding={this.constructViewBinding()}/>;
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.newElement; page = this.newElement;
console.log(page); console.log(page);
} else if (this.state.page === "lastpage") { } else if (this.state.page === 'lastpage') {
// return back to last page // return back to last page
page = this.mypage; page = this.mypage;
} else { } else {
@ -102,20 +102,20 @@ class App extends React.Component {
<div className={[style.navcontainer, themeStyle.backgroundcolor, themeStyle.textcolor, themeStyle.hrcolor].join(' ')}> <div className={[style.navcontainer, themeStyle.backgroundcolor, themeStyle.textcolor, themeStyle.hrcolor].join(' ')}>
<div className={style.navbrand}>{this.state.mediacentername}</div> <div className={style.navbrand}>{this.state.mediacentername}</div>
<div className={[style.navitem, themeStyle.navitem, this.state.page === "default" ? style.navitemselected : {}].join(' ')} <div className={[style.navitem, themeStyle.navitem, this.state.page === 'default' ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "default"})}>Home onClick={() => this.setState({page: 'default'})}>Home
</div> </div>
<div className={[style.navitem, themeStyle.navitem, this.state.page === "random" ? style.navitemselected : {}].join(' ')} <div className={[style.navitem, themeStyle.navitem, this.state.page === 'random' ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "random"})}>Random Video onClick={() => this.setState({page: 'random'})}>Random Video
</div> </div>
<div className={[style.navitem, themeStyle.navitem, this.state.page === "categories" ? style.navitemselected : {}].join(' ')} <div className={[style.navitem, themeStyle.navitem, this.state.page === 'categories' ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "categories"})}>Categories onClick={() => this.setState({page: 'categories'})}>Categories
</div> </div>
<div className={[style.navitem, themeStyle.navitem, this.state.page === "settings" ? style.navitemselected : {}].join(' ')} <div className={[style.navitem, themeStyle.navitem, this.state.page === 'settings' ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "settings"})}>Settings onClick={() => this.setState({page: 'settings'})}>Settings
</div> </div>
</div> </div>
{this.state.generalSettingsLoaded ? this.MainBody() : "loading"} {this.state.generalSettingsLoaded ? this.MainBody() : 'loading'}
</div> </div>
); );
} }
@ -127,7 +127,7 @@ class App extends React.Component {
this.newElement = element; this.newElement = element;
this.setState({ this.setState({
page: "video" page: 'video'
}); });
} }
@ -136,7 +136,7 @@ class App extends React.Component {
*/ */
returnToLastElement() { returnToLastElement() {
this.setState({ this.setState({
page: "lastpage" page: 'lastpage'
}); });
} }
} }

View File

@ -37,8 +37,8 @@
} }
.navcontainer { .navcontainer {
border-width: 0 0 2px 0;
border-style: dotted; border-style: dotted;
border-width: 0 0 2px 0;
padding-bottom: 40px; padding-bottom: 40px;
padding-top: 20px; padding-top: 20px;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import App from './App'; import App from './App';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
describe('<App/>', function () { describe('<App/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -24,7 +24,7 @@ describe('<App/>', function () {
wrapper.instance().changeRootElement(<div id='testit'/>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find('#testit')).toHaveLength(1);
}); });
it('test hide video again', function () { it('test hide video again', function () {
@ -33,61 +33,61 @@ describe('<App/>', function () {
wrapper.instance().changeRootElement(<div id='testit'/>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find('#testit')).toHaveLength(1);
wrapper.instance().returnToLastElement(); wrapper.instance().returnToLastElement();
expect(wrapper.find("HomePage")).toHaveLength(1); expect(wrapper.find('HomePage')).toHaveLength(1);
}); });
it('test fallback to last loaded page', function () { it('test fallback to last loaded page', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
wrapper.find(".navitem").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click"); wrapper.find('.navitem').findWhere(t => t.text() === 'Random Video' && t.type() === 'div').simulate('click');
wrapper.instance().changeRootElement(<div id='testit'/>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find('#testit')).toHaveLength(1);
wrapper.instance().returnToLastElement(); wrapper.instance().returnToLastElement();
expect(wrapper.find("RandomPage")).toHaveLength(1); expect(wrapper.find('RandomPage')).toHaveLength(1);
}); });
it('test home click', function () { it('test home click', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
wrapper.setState({page: "wrongvalue"}); wrapper.setState({page: 'wrongvalue'});
expect(wrapper.find("HomePage")).toHaveLength(0); expect(wrapper.find('HomePage')).toHaveLength(0);
wrapper.find(".navitem").findWhere(t => t.text() === "Home" && t.type() === "div").simulate("click"); wrapper.find('.navitem').findWhere(t => t.text() === 'Home' && t.type() === 'div').simulate('click');
expect(wrapper.find("HomePage")).toHaveLength(1); expect(wrapper.find('HomePage')).toHaveLength(1);
}); });
it('test category click', function () { it('test category click', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
expect(wrapper.find("CategoryPage")).toHaveLength(0); expect(wrapper.find('CategoryPage')).toHaveLength(0);
wrapper.find(".navitem").findWhere(t => t.text() === "Categories" && t.type() === "div").simulate("click"); wrapper.find('.navitem').findWhere(t => t.text() === 'Categories' && t.type() === 'div').simulate('click');
expect(wrapper.find("CategoryPage")).toHaveLength(1); expect(wrapper.find('CategoryPage')).toHaveLength(1);
}); });
it('test settings click', function () { it('test settings click', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
expect(wrapper.find("SettingsPage")).toHaveLength(0); expect(wrapper.find('SettingsPage')).toHaveLength(0);
wrapper.find(".navitem").findWhere(t => t.text() === "Settings" && t.type() === "div").simulate("click"); wrapper.find('.navitem').findWhere(t => t.text() === 'Settings' && t.type() === 'div').simulate('click');
expect(wrapper.find("SettingsPage")).toHaveLength(1); expect(wrapper.find('SettingsPage')).toHaveLength(1);
}); });
it('test initial fetch from api', done => { it('test initial fetch from api', done => {
global.fetch = global.prepareFetchApi({ global.fetch = global.prepareFetchApi({
generalSettingsLoaded: true, generalSettingsLoaded: true,
passwordsupport: true, passwordsupport: true,
mediacentername: "testname" mediacentername: 'testname'
}); });
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);

View File

@ -1,5 +1,5 @@
import darktheme from "./AppDarkTheme.module.css"; import darktheme from './AppDarkTheme.module.css';
import lighttheme from "./AppLightTheme.module.css"; import lighttheme from './AppLightTheme.module.css';
/** /**
* This class is available for all components in project * This class is available for all components in project

View File

@ -1,5 +1,5 @@
import React from "react"; import React from 'react';
import GlobalInfos from "./GlobalInfos"; import GlobalInfos from './GlobalInfos';
describe('<GlobalInfos/>', function () { describe('<GlobalInfos/>', function () {
it('always same instance ', function () { it('always same instance ', function () {

View File

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import ReactDom from 'react-dom'; import ReactDom from 'react-dom';
import style from './AddTagPopup.module.css' import style from './AddTagPopup.module.css';
import Tag from "../Tag/Tag"; import Tag from '../Tag/Tag';
import {Line} from "../PageTitle/PageTitle"; import {Line} from '../PageTitle/PageTitle';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
/** /**
* component creates overlay to add a new tag to a video * component creates overlay to add a new tag to a video
@ -84,7 +84,7 @@ class AddTagPopup extends React.Component {
*/ */
keypress(event) { keypress(event) {
// hide if escape is pressed // hide if escape is pressed
if (event.key === "Escape") { if (event.key === 'Escape') {
this.props.onHide(); this.props.onHide();
} }
} }
@ -95,7 +95,7 @@ class AddTagPopup extends React.Component {
* @param tagname tag name to add * @param tagname tag name to add
*/ */
addTag(tagid, tagname) { addTag(tagid, tagname) {
console.log(this.props) console.log(this.props);
const updateRequest = new FormData(); const updateRequest = new FormData();
updateRequest.append('action', 'addTag'); updateRequest.append('action', 'addTag');
updateRequest.append('id', tagid); updateRequest.append('id', tagid);
@ -104,8 +104,8 @@ class AddTagPopup extends React.Component {
fetch('/api/tags.php', {method: 'POST', body: updateRequest}) fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
if (result.result !== "success") { if (result.result !== 'success') {
console.log("error occured while writing to db -- todo error handling"); console.log('error occured while writing to db -- todo error handling');
console.log(result.result); console.log(result.result);
} else { } else {
this.props.submit(tagid, tagname); this.props.submit(tagid, tagname);
@ -142,8 +142,8 @@ class AddTagPopup extends React.Component {
xOld = e.clientX; xOld = e.clientX;
yOld = e.clientY; yOld = e.clientY;
// set the element's new position: // set the element's new position:
elmnt.style.top = (elmnt.offsetTop - dy) + "px"; elmnt.style.top = (elmnt.offsetTop - dy) + 'px';
elmnt.style.left = (elmnt.offsetLeft - dx) + "px"; elmnt.style.left = (elmnt.offsetLeft - dx) + 'px';
} }
function closeDragElement() { function closeDragElement() {

View File

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
import "@testing-library/jest-dom" import '@testing-library/jest-dom';
import AddTagPopup from "./AddTagPopup"; import AddTagPopup from './AddTagPopup';
describe('<AddTagPopup/>', function () { describe('<AddTagPopup/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -14,10 +14,10 @@ describe('<AddTagPopup/>', function () {
it('test tag insertion', function () { it('test tag insertion', function () {
const wrapper = shallow(<AddTagPopup/>); const wrapper = shallow(<AddTagPopup/>);
wrapper.setState({ wrapper.setState({
items: [{tag_id: 1, tag_name: 'test'}, {tag_id: 2, tag_name: "ee"}] items: [{tag_id: 1, tag_name: 'test'}, {tag_id: 2, tag_name: 'ee'}]
}, () => { }, () => {
expect(wrapper.find('Tag')).toHaveLength(2); expect(wrapper.find('Tag')).toHaveLength(2);
expect(wrapper.find('Tag').first().dive().text()).toBe("test"); expect(wrapper.find('Tag').first().dive().text()).toBe('test');
}); });
}); });
@ -36,13 +36,13 @@ describe('<AddTagPopup/>', function () {
it('test addtag', done => { it('test addtag', done => {
const wrapper = shallow(<AddTagPopup/>); const wrapper = shallow(<AddTagPopup/>);
global.fetch = prepareFetchApi({result: "success"}); global.fetch = prepareFetchApi({result: 'success'});
wrapper.setProps({ wrapper.setProps({
submit: jest.fn(() => {}), submit: jest.fn(() => {}),
onHide: jest.fn() onHide: jest.fn()
}, () => { }, () => {
wrapper.instance().addTag(1, "test"); wrapper.instance().addTag(1, 'test');
expect(global.fetch).toHaveBeenCalledTimes(1); expect(global.fetch).toHaveBeenCalledTimes(1);
}); });
@ -59,13 +59,13 @@ describe('<AddTagPopup/>', function () {
it('test failing addTag', done => { it('test failing addTag', done => {
const wrapper = shallow(<AddTagPopup/>); const wrapper = shallow(<AddTagPopup/>);
global.fetch = prepareFetchApi({result: "fail"}); global.fetch = prepareFetchApi({result: 'fail'});
wrapper.setProps({ wrapper.setProps({
submit: jest.fn(() => {}), submit: jest.fn(() => {}),
onHide: jest.fn() onHide: jest.fn()
}, () => { }, () => {
wrapper.instance().addTag(1, "test"); wrapper.instance().addTag(1, 'test');
expect(global.fetch).toHaveBeenCalledTimes(1); expect(global.fetch).toHaveBeenCalledTimes(1);
}); });

View File

@ -1,7 +1,7 @@
import React from "react"; import React from 'react';
import style from './InfoHeaderItem.module.css'; import style from './InfoHeaderItem.module.css';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {Spinner} from "react-bootstrap"; import {Spinner} from 'react-bootstrap';
/** /**
* a component to display one of the short quickinfo tiles on dashboard * a component to display one of the short quickinfo tiles on dashboard
@ -15,8 +15,8 @@ class InfoHeaderItem extends React.Component {
}} className={style.infoheaderitem} style={{backgroundColor: this.props.backColor}}> }} className={style.infoheaderitem} style={{backgroundColor: this.props.backColor}}>
<div className={style.icon}> <div className={style.icon}>
<FontAwesomeIcon style={{ <FontAwesomeIcon style={{
verticalAlign: "middle", verticalAlign: 'middle',
lineHeight: "130px" lineHeight: '130px'
}} icon={this.props.icon} size='5x'/> }} icon={this.props.icon} size='5x'/>
</div> </div>
{this.props.text !== null && this.props.text !== undefined ? {this.props.text !== null && this.props.text !== undefined ?

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import InfoHeaderItem from "./InfoHeaderItem"; import InfoHeaderItem from './InfoHeaderItem';
describe('<InfoHeaderItem/>', function () { describe('<InfoHeaderItem/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -10,34 +10,34 @@ describe('<InfoHeaderItem/>', function () {
it('renders correct text', function () { it('renders correct text', function () {
const wrapper = shallow(<InfoHeaderItem text='mytext'/>); const wrapper = shallow(<InfoHeaderItem text='mytext'/>);
expect(wrapper.find(".maintext").text()).toBe("mytext"); expect(wrapper.find('.maintext').text()).toBe('mytext');
}); });
it('renders correct subtext', function () { it('renders correct subtext', function () {
const wrapper = shallow(<InfoHeaderItem text='mimi' subtext='testtext'/>); const wrapper = shallow(<InfoHeaderItem text='mimi' subtext='testtext'/>);
expect(wrapper.find(".subtext").text()).toBe("testtext"); expect(wrapper.find('.subtext').text()).toBe('testtext');
}); });
it('test no subtext if no text defined', function () { it('test no subtext if no text defined', function () {
const wrapper = shallow(<InfoHeaderItem subtext='testi'/>); const wrapper = shallow(<InfoHeaderItem subtext='testi'/>);
expect(wrapper.find(".subtext")).toHaveLength(0); expect(wrapper.find('.subtext')).toHaveLength(0);
}); });
it('test custom click handler', function () { it('test custom click handler', function () {
const func = jest.fn(); const func = jest.fn();
const wrapper = shallow(<InfoHeaderItem onClick={() => func()}/>); const wrapper = shallow(<InfoHeaderItem onClick={() => func()}/>);
expect(func).toBeCalledTimes(0); expect(func).toBeCalledTimes(0);
wrapper.simulate("click"); wrapper.simulate('click');
expect(func).toBeCalledTimes(1); expect(func).toBeCalledTimes(1);
}); });
it('test insertion of loading spinner', function () { it('test insertion of loading spinner', function () {
const wrapper = shallow(<InfoHeaderItem text={null}/>); const wrapper = shallow(<InfoHeaderItem text={null}/>);
expect(wrapper.find("Spinner").length).toBe(1); expect(wrapper.find('Spinner').length).toBe(1);
}); });
it('test loading spinner if undefined', function () { it('test loading spinner if undefined', function () {
const wrapper = shallow(<InfoHeaderItem/>); const wrapper = shallow(<InfoHeaderItem/>);
expect(wrapper.find("Spinner").length).toBe(1); expect(wrapper.find('Spinner').length).toBe(1);
}); });
}); });

View File

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import Modal from 'react-bootstrap/Modal' import Modal from 'react-bootstrap/Modal';
import {Form} from "react-bootstrap"; import {Form} from 'react-bootstrap';
/** /**
* creates modal overlay to define a new Tag * creates modal overlay to define a new Tag
@ -30,7 +30,7 @@ class NewTagPopup extends React.Component {
<Form.Group> <Form.Group>
<Form.Label>Tag Name:</Form.Label> <Form.Label>Tag Name:</Form.Label>
<Form.Control id='namefield' type='text' placeholder='Enter Tag name' onChange={(v) => { <Form.Control id='namefield' type='text' placeholder='Enter Tag name' onChange={(v) => {
this.value = v.target.value this.value = v.target.value;
}}/> }}/>
<Form.Text className='text-muted'> <Form.Text className='text-muted'>
This Tag will automatically show up on category page. This Tag will automatically show up on category page.
@ -59,8 +59,8 @@ class NewTagPopup extends React.Component {
fetch('/api/tags.php', {method: 'POST', body: updateRequest}) fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json()) .then((response) => response.json())
.then((result) => { .then((result) => {
if (result.result !== "success") { if (result.result !== 'success') {
console.log("error occured while writing to db -- todo error handling"); console.log('error occured while writing to db -- todo error handling');
console.log(result.result); console.log(result.result);
} }
this.props.onHide(); this.props.onHide();

View File

@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
import "@testing-library/jest-dom" import '@testing-library/jest-dom';
import NewTagPopup from "./NewTagPopup"; import NewTagPopup from './NewTagPopup';
describe('<NewTagPopup/>', function () { describe('<NewTagPopup/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -14,7 +14,7 @@ describe('<NewTagPopup/>', function () {
const mockSuccessResponse = {}; const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse); const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({ const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise, json: () => mockJsonPromise
}); });
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise); global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
@ -23,7 +23,7 @@ describe('<NewTagPopup/>', function () {
const wrapper = shallow(<NewTagPopup/>); const wrapper = shallow(<NewTagPopup/>);
wrapper.setProps({ wrapper.setProps({
onHide: () => { onHide: () => {
func() func();
} }
}); });

View File

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import style from "./PageTitle.module.css" import style from './PageTitle.module.css';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
/** /**
* Component for generating PageTitle with bottom Line * Component for generating PageTitle with bottom Line

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
import PageTitle from "./PageTitle"; import PageTitle from './PageTitle';
describe('<Preview/>', function () { describe('<Preview/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -13,19 +13,19 @@ describe('<Preview/>', function () {
const wrapper = shallow(<PageTitle>heyimachild</PageTitle>); const wrapper = shallow(<PageTitle>heyimachild</PageTitle>);
const children = wrapper.children(); const children = wrapper.children();
expect(children.at(children.length - 2).text()).toBe("heyimachild"); expect(children.at(children.length - 2).text()).toBe('heyimachild');
}); });
it('renders pagetitle prop', function () { it('renders pagetitle prop', function () {
const wrapper = shallow(<PageTitle title='testtitle'/>); const wrapper = shallow(<PageTitle title='testtitle'/>);
expect(wrapper.find(".pageheader").text()).toBe("testtitle<Line />"); expect(wrapper.find('.pageheader').text()).toBe('testtitle<Line />');
}); });
it('renders subtitle prop', function () { it('renders subtitle prop', function () {
const wrapper = shallow(<PageTitle subtitle='testsubtitle'/>); const wrapper = shallow(<PageTitle subtitle='testsubtitle'/>);
expect(wrapper.find(".pageheadersubtitle").text()).toBe("testsubtitle"); expect(wrapper.find('.pageheadersubtitle').text()).toBe('testsubtitle');
}); });
}); });

View File

@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import style from "./Preview.module.css"; import style from './Preview.module.css';
import Player from "../../pages/Player/Player"; import Player from '../../pages/Player/Player';
import {Spinner} from "react-bootstrap"; import {Spinner} from 'react-bootstrap';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
/** /**
* Component for single preview tile * Component for single preview tile
@ -62,7 +62,7 @@ class Preview extends React.Component {
* handle the click event of a tile * handle the click event of a tile
*/ */
itemClick() { itemClick() {
console.log("item clicked!" + this.state.name); console.log('item clicked!' + this.state.name);
this.props.viewbinding.changeRootElement( this.props.viewbinding.changeRootElement(
<Player <Player

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
import Preview, {TagPreview} from "./Preview"; import Preview, {TagPreview} from './Preview';
describe('<Preview/>', function () { describe('<Preview/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -23,7 +23,7 @@ describe('<Preview/>', function () {
wrapper.setProps({ wrapper.setProps({
viewbinding: { viewbinding: {
changeRootElement: () => { changeRootElement: () => {
func() func();
} }
} }
}); });
@ -38,7 +38,7 @@ describe('<Preview/>', function () {
const mockSuccessResponse = 'testsrc'; const mockSuccessResponse = 'testsrc';
const mockJsonPromise = Promise.resolve(mockSuccessResponse); const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({ const mockFetchPromise = Promise.resolve({
text: () => mockJsonPromise, text: () => mockJsonPromise
}); });
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise); global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
@ -49,7 +49,7 @@ describe('<Preview/>', function () {
process.nextTick(() => { process.nextTick(() => {
// received picture should be rendered into wrapper // received picture should be rendered into wrapper
expect(wrapper.find(".previewimage").props().src).not.toBeNull(); expect(wrapper.find('.previewimage').props().src).not.toBeNull();
global.fetch.mockClear(); global.fetch.mockClear();
done(); done();
@ -61,7 +61,7 @@ describe('<Preview/>', function () {
const wrapper = shallow(<Preview/>); const wrapper = shallow(<Preview/>);
// expect load animation to be visible // expect load animation to be visible
expect(wrapper.find(".loadAnimation")).toHaveLength(1); expect(wrapper.find('.loadAnimation')).toHaveLength(1);
}); });
}); });
@ -84,7 +84,7 @@ describe('<TagPreview/>', function () {
const wrapper = shallow(<TagPreview/>); const wrapper = shallow(<TagPreview/>);
wrapper.setProps({ wrapper.setProps({
categorybinding: () => { categorybinding: () => {
func() func();
} }
}); });

View File

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import style from "./SideBar.module.css" import style from './SideBar.module.css';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
/** /**
* component for sidebar-info * component for sidebar-info

View File

@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import SideBar, {SideBarItem, SideBarTitle} from "./SideBar"; import SideBar, {SideBarItem, SideBarTitle} from './SideBar';
import "@testing-library/jest-dom" import '@testing-library/jest-dom';
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
describe('<SideBar/>', function () { describe('<SideBar/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -12,16 +12,16 @@ describe('<SideBar/>', function () {
it('renders childs correctly', function () { it('renders childs correctly', function () {
const wrapper = shallow(<SideBar>test</SideBar>); const wrapper = shallow(<SideBar>test</SideBar>);
expect(wrapper.children().text()).toBe("test"); expect(wrapper.children().text()).toBe('test');
}); });
it('sidebar Item renders without crashing', function () { it('sidebar Item renders without crashing', function () {
const wrapper = shallow(<SideBarItem>Test</SideBarItem>); const wrapper = shallow(<SideBarItem>Test</SideBarItem>);
expect(wrapper.children().text()).toBe("Test"); expect(wrapper.children().text()).toBe('Test');
}); });
it('renderes sidebartitle correctly', function () { it('renderes sidebartitle correctly', function () {
const wrapper = shallow(<SideBarTitle>Test</SideBarTitle>); const wrapper = shallow(<SideBarTitle>Test</SideBarTitle>);
expect(wrapper.children().text()).toBe("Test"); expect(wrapper.children().text()).toBe('Test');
}); });
}); });

View File

@ -1,7 +1,7 @@
import React from "react"; import React from 'react';
import styles from "./Tag.module.css" import styles from './Tag.module.css';
import CategoryPage from "../../pages/CategoryPage/CategoryPage"; import CategoryPage from '../../pages/CategoryPage/CategoryPage';
/** /**
* A Component representing a single Category tag * A Component representing a single Category tag

View File

@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import Tag from './Tag' import Tag from './Tag';
import "@testing-library/jest-dom" import '@testing-library/jest-dom';
import {shallow} from 'enzyme' import {shallow} from 'enzyme';
describe('<Tag/>', function () { describe('<Tag/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -12,7 +12,7 @@ describe('<Tag/>', function () {
it('renders childs correctly', function () { it('renders childs correctly', 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 () { it('click event triggered and setvideo callback called', function () {
@ -27,7 +27,7 @@ describe('<Tag/>', function () {
expect(func).toBeCalledTimes(0); expect(func).toBeCalledTimes(0);
wrapper.simulate("click"); wrapper.simulate('click');
expect(func).toBeCalledTimes(1); expect(func).toBeCalledTimes(1);
}); });
@ -36,11 +36,11 @@ describe('<Tag/>', function () {
const func = jest.fn(); const func = jest.fn();
const wrapper = shallow(<Tag const wrapper = shallow(<Tag
onclick={() => {func()}}>test</Tag>); onclick={() => {func();}}>test</Tag>);
expect(func).toBeCalledTimes(0); expect(func).toBeCalledTimes(0);
wrapper.simulate("click"); wrapper.simulate('click');
expect(func).toBeCalledTimes(1); expect(func).toBeCalledTimes(1);
}); });

View File

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import Preview from "../Preview/Preview"; import Preview from '../Preview/Preview';
import style from "./VideoContainer.module.css" import style from './VideoContainer.module.css';
/** /**
* A videocontainer storing lots of Preview elements * A videocontainer storing lots of Preview elements
@ -39,7 +39,7 @@ class VideoContainer extends React.Component {
))} ))}
{/*todo css for no items to show*/} {/*todo css for no items to show*/}
{this.state.loadeditems.length === 0 ? {this.state.loadeditems.length === 0 ?
"no items to show!" : null} 'no items to show!' : null}
{this.props.children} {this.props.children}
</div> </div>
); );
@ -56,7 +56,7 @@ class VideoContainer extends React.Component {
* @param nr number of previews to load * @param nr number of previews to load
*/ */
loadPreviewBlock(nr) { loadPreviewBlock(nr) {
console.log("loadpreviewblock called ...") console.log('loadpreviewblock called ...');
let ret = []; let ret = [];
for (let i = 0; i < nr; i++) { for (let i = 0; i < nr; i++) {
// only add if not end // only add if not end
@ -84,7 +84,7 @@ class VideoContainer extends React.Component {
if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) { if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) {
this.loadPreviewBlock(8); this.loadPreviewBlock(8);
} }
} };
} }
export default VideoContainer; export default VideoContainer;

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import VideoContainer from "./VideoContainer"; import VideoContainer from './VideoContainer';
describe('<VideoContainer/>', function () { describe('<VideoContainer/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -25,6 +25,6 @@ describe('<VideoContainer/>', function () {
it('no items available', () => { it('no items available', () => {
const wrapper = shallow(<VideoContainer data={[]}/>); const wrapper = shallow(<VideoContainer data={[]}/>);
expect(wrapper.find('Preview')).toHaveLength(0); expect(wrapper.find('Preview')).toHaveLength(0);
expect(wrapper.find(".maincontent").text()).toBe("no items to show!"); expect(wrapper.find('.maincontent').text()).toBe('no items to show!');
}); });
}); });

View File

@ -1,12 +1,12 @@
import React from "react"; import React from 'react';
import SideBar, {SideBarTitle} from "../../elements/SideBar/SideBar"; import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from "../../elements/Tag/Tag"; import Tag from '../../elements/Tag/Tag';
import videocontainerstyle from "../../elements/VideoContainer/VideoContainer.module.css" import videocontainerstyle from '../../elements/VideoContainer/VideoContainer.module.css';
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, {Line} from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from '../../elements/VideoContainer/VideoContainer';
/** /**
* Component for Category Page * Component for Category Page
@ -40,33 +40,33 @@ class CategoryPage extends React.Component {
<> <>
<PageTitle <PageTitle
title='Categories' title='Categories'
subtitle={!this.state.selected ? this.state.loadedtags.length + " different Tags" : this.state.selected}/> subtitle={!this.state.selected ? this.state.loadedtags.length + ' different Tags' : this.state.selected}/>
<SideBar> <SideBar>
<SideBarTitle>Default Tags:</SideBarTitle> <SideBarTitle>Default Tags:</SideBarTitle>
<Tag viewbinding={{ <Tag viewbinding={{
changeRootElement: (e) => { changeRootElement: (e) => {
this.loadTag(e.props.category) this.loadTag(e.props.category);
} }
}}>All</Tag> }}>All</Tag>
<Tag viewbinding={{ <Tag viewbinding={{
changeRootElement: (e) => { changeRootElement: (e) => {
this.loadTag(e.props.category) this.loadTag(e.props.category);
} }
}}>FullHd</Tag> }}>FullHd</Tag>
<Tag viewbinding={{ <Tag viewbinding={{
changeRootElement: (e) => { changeRootElement: (e) => {
this.loadTag(e.props.category) this.loadTag(e.props.category);
} }
}}>LowQuality</Tag> }}>LowQuality</Tag>
<Tag viewbinding={{ <Tag viewbinding={{
changeRootElement: (e) => { changeRootElement: (e) => {
this.loadTag(e.props.category) this.loadTag(e.props.category);
} }
}}>HD</Tag> }}>HD</Tag>
<Line/> <Line/>
<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});
}}>Add a new Tag! }}>Add a new Tag!
</button> </button>
</SideBar></> </SideBar></>
@ -98,7 +98,7 @@ class CategoryPage extends React.Component {
viewbinding={this.props.viewbinding} viewbinding={this.props.viewbinding}
categorybinding={this.loadTag}/> categorybinding={this.loadTag}/>
)) : )) :
"loading"} 'loading'}
</div> </div>
} }
@ -133,7 +133,7 @@ class CategoryPage extends React.Component {
updateRequest.append('action', 'getMovies'); updateRequest.append('action', 'getMovies');
updateRequest.append('tag', tag); updateRequest.append('tag', tag);
console.log("fetching data"); console.log('fetching data');
// fetch all videos available // fetch all videos available
fetch('/api/video.php', {method: 'POST', body: updateRequest}) fetch('/api/video.php', {method: 'POST', body: updateRequest})
@ -144,7 +144,7 @@ class CategoryPage extends React.Component {
this.setState({selected: tag}); this.setState({selected: tag});
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
@ -170,7 +170,7 @@ class CategoryPage extends React.Component {
this.setState({loadedtags: result}); this.setState({loadedtags: result});
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import {mount, shallow} from "enzyme"; import {mount, shallow} from 'enzyme';
import React from "react"; import React from 'react';
import CategoryPage from "./CategoryPage"; import CategoryPage from './CategoryPage';
describe('<CategoryPage/>', function () { describe('<CategoryPage/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -9,7 +9,7 @@ describe('<CategoryPage/>', function () {
}); });
it('test tag fetch call', done => { it('test tag fetch call', done => {
global.fetch = global.prepareFetchApi(["first", "second"]); global.fetch = global.prepareFetchApi(['first', 'second']);
const wrapper = shallow(<CategoryPage/>); const wrapper = shallow(<CategoryPage/>);
@ -38,7 +38,7 @@ describe('<CategoryPage/>', function () {
process.nextTick(() => { process.nextTick(() => {
//callback to close window should have called //callback to close window should have called
expect(message).toBe("no connection to backend"); expect(message).toBe('no connection to backend');
global.fetch.mockClear(); global.fetch.mockClear();
done(); done();
@ -48,14 +48,14 @@ describe('<CategoryPage/>', function () {
it('test new tag popup', function () { it('test new tag popup', function () {
const wrapper = mount(<CategoryPage/>); const wrapper = mount(<CategoryPage/>);
expect(wrapper.find("NewTagPopup")).toHaveLength(0); expect(wrapper.find('NewTagPopup')).toHaveLength(0);
wrapper.find('[data-testid="btnaddtag"]').simulate('click'); wrapper.find('[data-testid="btnaddtag"]').simulate('click');
// newtagpopup should be showing now // newtagpopup should be showing now
expect(wrapper.find("NewTagPopup")).toHaveLength(1); expect(wrapper.find('NewTagPopup')).toHaveLength(1);
// click close button in modal // click close button in modal
wrapper.find(".modal-header").find("button").simulate("click"); wrapper.find('.modal-header').find('button').simulate('click');
expect(wrapper.find("NewTagPopup")).toHaveLength(0); expect(wrapper.find('NewTagPopup')).toHaveLength(0);
}); });
it('test setpage callback', done => { it('test setpage callback', done => {
@ -66,17 +66,17 @@ describe('<CategoryPage/>', function () {
wrapper.setState({ wrapper.setState({
loadedtags: [ loadedtags: [
{ {
tag_name: "testname", tag_name: 'testname',
tag_id: 42 tag_id: 42
} }
] ]
}); });
wrapper.find("TagPreview").find("div").first().simulate("click"); wrapper.find('TagPreview').find('div').first().simulate('click');
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');
global.fetch.mockClear(); global.fetch.mockClear();
done(); done();
@ -87,10 +87,10 @@ describe('<CategoryPage/>', function () {
const wrapper = shallow(<CategoryPage/>); const wrapper = shallow(<CategoryPage/>);
wrapper.setState({ wrapper.setState({
selected: "test" selected: 'test'
}); });
expect(wrapper.state().selected).not.toBeNull(); expect(wrapper.state().selected).not.toBeNull();
wrapper.find('[data-testid="backbtn"]').simulate("click"); wrapper.find('[data-testid="backbtn"]').simulate('click');
expect(wrapper.state().selected).toBeNull(); expect(wrapper.state().selected).toBeNull();
}); });
@ -112,9 +112,9 @@ describe('<CategoryPage/>', function () {
console.log(wrapper.debug()); console.log(wrapper.debug());
expect(func).toBeCalledTimes(0); expect(func).toBeCalledTimes(0);
wrapper.find("SideBar").find("Tag").forEach(e => { wrapper.find('SideBar').find('Tag').forEach(e => {
e.simulate("click"); e.simulate('click');
}) });
expect(func).toBeCalledTimes(4); expect(func).toBeCalledTimes(4);

View File

@ -1,17 +1,17 @@
import React from "react"; import React from 'react';
import SideBar, {SideBarItem, SideBarTitle} from "../../elements/SideBar/SideBar"; import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from "../../elements/Tag/Tag"; import Tag from '../../elements/Tag/Tag';
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from '../../elements/VideoContainer/VideoContainer';
import style from "./HomePage.module.css" import style from './HomePage.module.css';
import PageTitle, {Line} from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
/** /**
* The home page component showing on the initial pageload * The home page component showing on the initial pageload
*/ */
class HomePage extends React.Component { class HomePage extends React.Component {
/** keyword variable needed temporary store search keyword */ /** keyword variable needed temporary store search keyword */
keyword = ""; keyword = '';
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
@ -24,7 +24,7 @@ class HomePage extends React.Component {
sdvideonr: null, sdvideonr: null,
tagnr: null tagnr: null
}, },
tag: "All", subtitle: 'All Videos',
data: [], data: [],
selectionnr: 0 selectionnr: 0
}; };
@ -32,7 +32,7 @@ class HomePage extends React.Component {
componentDidMount() { componentDidMount() {
// initial get of all videos // initial get of all videos
this.fetchVideoData("all"); this.fetchVideoData('All');
this.fetchStartData(); this.fetchStartData();
} }
@ -47,7 +47,7 @@ class HomePage extends React.Component {
updateRequest.append('action', 'getMovies'); updateRequest.append('action', 'getMovies');
updateRequest.append('tag', tag); updateRequest.append('tag', tag);
console.log("fetching data"); console.log('fetching data from' + tag);
// fetch all videos available // fetch all videos available
fetch('/api/video.php', {method: 'POST', body: updateRequest}) fetch('/api/video.php', {method: 'POST', body: updateRequest})
@ -58,11 +58,12 @@ class HomePage extends React.Component {
}); });
this.setState({ this.setState({
data: result, data: result,
selectionnr: result.length selectionnr: result.length,
tag: tag + ' Videos'
}); });
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
@ -88,7 +89,7 @@ class HomePage extends React.Component {
}); });
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
@ -98,7 +99,7 @@ class HomePage extends React.Component {
* @param keyword The keyword to search for * @param keyword The keyword to search for
*/ */
searchVideos(keyword) { searchVideos(keyword) {
console.log("search called"); console.log('search called');
const updateRequest = new FormData(); const updateRequest = new FormData();
updateRequest.append('action', 'getSearchKeyWord'); updateRequest.append('action', 'getSearchKeyWord');
@ -113,11 +114,12 @@ class HomePage extends React.Component {
}); });
this.setState({ this.setState({
data: result, data: result,
selectionnr: result.length selectionnr: result.length,
tag: 'Search result: ' + keyword
}); });
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
@ -126,15 +128,15 @@ class HomePage extends React.Component {
<> <>
<PageTitle <PageTitle
title='Home Page' title='Home Page'
subtitle={this.state.tag + " Videos - " + this.state.selectionnr}> subtitle={this.state.subtitle + ' - ' + this.state.selectionnr}>
<form className={"form-inline " + style.searchform} onSubmit={(e) => { <form className={'form-inline ' + style.searchform} onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
this.searchVideos(this.keyword); this.searchVideos(this.keyword);
}}> }}>
<input data-testid='searchtextfield' className='form-control mr-sm-2' <input data-testid='searchtextfield' className='form-control mr-sm-2'
type='text' placeholder='Search' type='text' placeholder='Search'
onChange={(e) => { onChange={(e) => {
this.keyword = e.target.value this.keyword = e.target.value;
}}/> }}/>
<button data-testid='searchbtnsubmit' className='btn btn-success' type='submit'>Search</button> <button data-testid='searchbtnsubmit' className='btn btn-success' type='submit'>Search</button>
</form> </form>
@ -149,10 +151,10 @@ class HomePage extends React.Component {
<SideBarItem><b>{this.state.sideinfo.tagnr}</b> different Tags!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.tagnr}</b> different Tags!</SideBarItem>
<Line/> <Line/>
<SideBarTitle>Default Tags:</SideBarTitle> <SideBarTitle>Default Tags:</SideBarTitle>
<Tag viewbinding={this.props.viewbinding}>All</Tag> <Tag onclick={() => this.fetchVideoData('All')}>All</Tag>
<Tag viewbinding={this.props.viewbinding}>FullHd</Tag> <Tag onclick={() => this.fetchVideoData('FullHd')}>FullHd</Tag>
<Tag viewbinding={this.props.viewbinding}>LowQuality</Tag> <Tag onclick={() => this.fetchVideoData('LowQuality')}>LowQuality</Tag>
<Tag viewbinding={this.props.viewbinding}>HD</Tag> <Tag onclick={() => this.fetchVideoData('HD')}>HD</Tag>
</SideBar> </SideBar>
{this.state.data.length !== 0 ? {this.state.data.length !== 0 ?
<VideoContainer <VideoContainer

View File

@ -1,7 +1,7 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import HomePage from "./HomePage"; import HomePage from './HomePage';
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from '../../elements/VideoContainer/VideoContainer';
describe('<HomePage/>', function () { describe('<HomePage/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -12,7 +12,7 @@ describe('<HomePage/>', function () {
it('test data insertion', function () { it('test data insertion', function () {
const wrapper = shallow(<HomePage/>); const wrapper = shallow(<HomePage/>);
expect(wrapper.find("VideoContainer")).toHaveLength(0); expect(wrapper.find('VideoContainer')).toHaveLength(0);
wrapper.setState({ wrapper.setState({
data: [ data: [
@ -21,20 +21,20 @@ describe('<HomePage/>', function () {
}); });
// there shoud be loaded the Videocontainer element into dom after fetching videos correctly // there shoud be loaded the Videocontainer element into dom after fetching videos correctly
expect(wrapper.find("VideoContainer")).toHaveLength(1); expect(wrapper.find('VideoContainer')).toHaveLength(1);
}); });
it('test title and nr insertions', function () { it('test title and nr insertions', function () {
const wrapper = shallow(<HomePage/>); const wrapper = shallow(<HomePage/>);
expect(wrapper.find("PageTitle").props().subtitle).toBe("All Videos - 0"); expect(wrapper.find('PageTitle').props().subtitle).toBe('All Videos - 0');
wrapper.setState({ wrapper.setState({
tag: "testtag", subtitle: 'testsubtitle',
selectionnr: 42 selectionnr: 42
}); });
expect(wrapper.find("PageTitle").props().subtitle).toBe("testtag Videos - 42"); expect(wrapper.find('PageTitle').props().subtitle).toBe('testsubtitle - 42');
}); });
it('test search field', done => { it('test search field', done => {
@ -43,7 +43,7 @@ describe('<HomePage/>', function () {
const wrapper = shallow(<HomePage/>); const wrapper = shallow(<HomePage/>);
wrapper.find('[data-testid="searchtextfield"]').simulate('change', {target: {value: 'testvalue'}}); wrapper.find('[data-testid="searchtextfield"]').simulate('change', {target: {value: 'testvalue'}});
wrapper.find('[data-testid="searchbtnsubmit"]').simulate("click"); wrapper.find('[data-testid="searchbtnsubmit"]').simulate('click');
process.nextTick(() => { process.nextTick(() => {
// state to be set correctly with response // state to be set correctly with response
@ -60,7 +60,7 @@ describe('<HomePage/>', function () {
const wrapper = shallow(<HomePage/>); const wrapper = shallow(<HomePage/>);
const fakeEvent = {preventDefault: () => console.log('preventDefault')}; const fakeEvent = {preventDefault: () => console.log('preventDefault')};
wrapper.find(".searchform").simulate('submit', fakeEvent); wrapper.find('.searchform').simulate('submit', fakeEvent);
expect(wrapper.state().selectionnr).toBe(0); expect(wrapper.state().selectionnr).toBe(0);
@ -92,4 +92,34 @@ describe('<HomePage/>', function () {
done(); done();
}); });
}); });
it('test tag click', done => {
global.fetch = prepareFetchApi(['test1', 'test2']);
const wrapper = shallow(<HomePage/>);
const tags = wrapper.find('SideBar').dive().find('Tag');
let i = 0;
function testBtn(e) {
e.dive().simulate('click');
process.nextTick(() => {
process.nextTick(() => {
// state to be set correctly with response
console.log('see ifits same');
expect(wrapper.state()).toMatchObject({data: ['test1', 'test2']});
wrapper.state.data = [];
i++;
if (i >= tags.length) {
done();
} else {
testBtn(tags.at(i));
}
});
});
}
testBtn(tags.first());
});
}); });

View File

@ -1,11 +1,11 @@
import React from "react"; import React from 'react';
import style from "./Player.module.css" import style from './Player.module.css';
import {PlyrComponent} from 'plyr-react'; import {PlyrComponent} from 'plyr-react';
import SideBar, {SideBarItem, SideBarTitle} from "../../elements/SideBar/SideBar"; import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from "../../elements/Tag/Tag"; import Tag from '../../elements/Tag/Tag';
import AddTagPopup from "../../elements/AddTagPopup/AddTagPopup"; import AddTagPopup from '../../elements/AddTagPopup/AddTagPopup';
import PageTitle, {Line} from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
/** /**
@ -26,7 +26,7 @@ class Player extends React.Component {
'settings', // Settings menu 'settings', // Settings menu
'airplay', // Airplay (currently Safari only) 'airplay', // Airplay (currently Safari only)
'download', // Show a download button with a link to either the current source or a custom URL you specify in your options 'download', // Show a download button with a link to either the current source or a custom URL you specify in your options
'fullscreen', // Toggle fullscreen 'fullscreen' // Toggle fullscreen
] ]
}; };
@ -66,19 +66,26 @@ class Player extends React.Component {
fetch('/api/tags.php', {method: 'POST', body: updateRequest}) fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
if (result.result !== "success") { if (result.result !== 'success') {
console.error("error occured while writing to db -- todo error handling"); console.error('error occured while writing to db -- todo error handling');
console.error(result.result); console.error(result.result);
} else { } else {
// check if tag has already been added
const tagindwx = this.state.tags.map(function (e) {
return e.tag_name;
}).indexOf(tag_name);
// only add tag if it isn't already there
if (tagindwx === -1) {
// update tags if successful // update tags if successful
let array = [...this.state.suggesttag]; // make a separate copy of the array let array = [...this.state.suggesttag]; // make a separate copy of the array (because of setState)
const index = array.map(function (e) { const quickaddindex = this.state.suggesttag.map(function (e) {
return e.tag_id; return e.tag_id;
}).indexOf(tag_id); }).indexOf(tag_id);
// check if tag is available in quickadds // check if tag is available in quickadds
if (index !== -1) { if (quickaddindex !== -1) {
array.splice(index, 1); array.splice(quickaddindex, 1);
this.setState({ this.setState({
tags: [...this.state.tags, {tag_name: tag_name}], tags: [...this.state.tags, {tag_name: tag_name}],
@ -90,6 +97,7 @@ class Player extends React.Component {
}); });
} }
} }
}
})); }));
} }
@ -170,7 +178,7 @@ class Player extends React.Component {
<button className='btn btn-info' onClick={() => this.setState({popupvisible: true})}>Give this <button className='btn btn-info' onClick={() => this.setState({popupvisible: true})}>Give this
Video a Tag Video a Tag
</button> </button>
<button className='btn btn-danger' onClick={() => {this.deleteVideo()}}>Delete Video</button> <button className='btn btn-danger' onClick={() => {this.deleteVideo();}}>Delete Video</button>
</div> </div>
</div> </div>
<button className={style.closebutton} onClick={() => this.closebtn()}>Close</button> <button className={style.closebutton} onClick={() => this.closebtn()}>Close</button>
@ -200,7 +208,7 @@ class Player extends React.Component {
{ {
src: result.movie_url, src: result.movie_url,
type: 'video/mp4', type: 'video/mp4',
size: 1080, size: 1080
} }
], ],
poster: result.thumbnail poster: result.thumbnail
@ -229,11 +237,11 @@ class Player extends React.Component {
fetch('/api/video.php', {method: 'POST', body: updateRequest}) fetch('/api/video.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
if (result.result === "success") { if (result.result === 'success') {
// likes +1 --> avoid reload of all data // likes +1 --> avoid reload of all data
this.setState({likes: this.state.likes + 1}) this.setState({likes: this.state.likes + 1});
} else { } else {
console.error("an error occured while liking"); console.error('an error occured while liking');
console.error(result); console.error(result);
} }
})); }));
@ -258,11 +266,11 @@ class Player extends React.Component {
fetch('/api/video.php', {method: 'POST', body: updateRequest}) fetch('/api/video.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
if (result.result === "success") { if (result.result === 'success') {
// return to last element if successful // return to last element if successful
this.props.viewbinding.returnToLastElement(); this.props.viewbinding.returnToLastElement();
} else { } else {
console.error("an error occured while liking"); console.error('an error occured while liking');
console.error(result); console.error(result);
} }
})); }));

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import Player from "./Player"; import Player from './Player';
describe('<Player/>', function () { describe('<Player/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -16,11 +16,11 @@ describe('<Player/>', function () {
{ {
src: '/tstvid.mp4', src: '/tstvid.mp4',
type: 'video/mp4', type: 'video/mp4',
size: 1080, size: 1080
} }
] ]
}); });
expect(wrapper.find("r")).toHaveLength(1); expect(wrapper.find('r')).toHaveLength(1);
}); });
function simulateLikeButtonClick() { function simulateLikeButtonClick() {
@ -28,7 +28,7 @@ describe('<Player/>', function () {
// initial fetch for getting movie data // initial fetch for getting movie data
expect(global.fetch).toHaveBeenCalledTimes(1); expect(global.fetch).toHaveBeenCalledTimes(1);
wrapper.find('.videoactions').find("button").first().simulate('click'); wrapper.find('.videoactions').find('button').first().simulate('click');
// fetch for liking // fetch for liking
expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenCalledTimes(2);
@ -69,20 +69,20 @@ describe('<Player/>', function () {
it('show tag popup', function () { it('show tag popup', function () {
const wrapper = shallow(<Player/>); const wrapper = shallow(<Player/>);
expect(wrapper.find("AddTagPopup")).toHaveLength(0); expect(wrapper.find('AddTagPopup')).toHaveLength(0);
// todo dynamic button find without index // todo dynamic button find without index
wrapper.find('.videoactions').find("button").at(1).simulate('click'); wrapper.find('.videoactions').find('button').at(1).simulate('click');
// addtagpopup should be showing now // addtagpopup should be showing now
expect(wrapper.find("AddTagPopup")).toHaveLength(1); expect(wrapper.find('AddTagPopup')).toHaveLength(1);
}); });
it('test delete button', done => { it('test delete button', done => {
const wrapper = shallow(<Player viewbinding={{ const wrapper = shallow(<Player viewbinding={{
returnToLastElement: jest.fn() returnToLastElement: jest.fn()
}}/>); }}/>);
global.fetch = prepareFetchApi({result: "success"}); global.fetch = prepareFetchApi({result: 'success'});
wrapper.find('.videoactions').find("button").at(2).simulate('click'); wrapper.find('.videoactions').find('button').at(2).simulate('click');
process.nextTick(() => { process.nextTick(() => {
// refetch is called so fetch called 3 times // refetch is called so fetch called 3 times
@ -101,7 +101,7 @@ describe('<Player/>', function () {
wrapper.setProps({ wrapper.setProps({
viewbinding: { viewbinding: {
returnToLastElement: () => { returnToLastElement: () => {
func() func();
} }
} }
}); });
@ -115,7 +115,7 @@ describe('<Player/>', function () {
it('inserts Tags correctly', function () { it('inserts Tags correctly', function () {
const wrapper = shallow(<Player/>); const wrapper = shallow(<Player/>);
expect(wrapper.find("Tag")).toHaveLength(0); expect(wrapper.find('Tag')).toHaveLength(0);
wrapper.setState({ wrapper.setState({
tags: [ tags: [
@ -124,7 +124,7 @@ describe('<Player/>', function () {
] ]
}); });
expect(wrapper.find("Tag")).toHaveLength(2); expect(wrapper.find('Tag')).toHaveLength(2);
}); });
it('inserts tag quickadd correctly', function () { it('inserts tag quickadd correctly', function () {
@ -137,7 +137,7 @@ describe('<Player/>', function () {
global.fetch = prepareFetchApi({result: 'success'}); global.fetch = prepareFetchApi({result: 'success'});
// render tag subcomponent // render tag subcomponent
const tag = wrapper.find("Tag").first().dive(); const tag = wrapper.find('Tag').first().dive();
tag.simulate('click'); tag.simulate('click');
process.nextTick(() => { process.nextTick(() => {
@ -155,7 +155,7 @@ describe('<Player/>', function () {
global.console.error = jest.fn(); global.console.error = jest.fn();
// render tag subcomponent // render tag subcomponent
const tag = wrapper.find("Tag").first().dive(); const tag = wrapper.find('Tag').first().dive();
tag.simulate('click'); tag.simulate('click');
process.nextTick(() => { process.nextTick(() => {
@ -169,15 +169,15 @@ describe('<Player/>', function () {
function generatetag() { function generatetag() {
const wrapper = shallow(<Player/>); const wrapper = shallow(<Player/>);
expect(wrapper.find("Tag")).toHaveLength(0); expect(wrapper.find('Tag')).toHaveLength(0);
wrapper.setState({ wrapper.setState({
suggesttag: [ suggesttag: [
{tag_name: 'first', tag_id: 1}, {tag_name: 'first', tag_id: 1}
] ]
}); });
expect(wrapper.find("Tag")).toHaveLength(1); expect(wrapper.find('Tag')).toHaveLength(1);
return wrapper; return wrapper;
} }

View File

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import style from "./RandomPage.module.css" import style from './RandomPage.module.css';
import SideBar, {SideBarTitle} from "../../elements/SideBar/SideBar"; import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from "../../elements/Tag/Tag"; import Tag from '../../elements/Tag/Tag';
import PageTitle from "../../elements/PageTitle/PageTitle"; import PageTitle from '../../elements/PageTitle/PageTitle';
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from '../../elements/VideoContainer/VideoContainer';
/** /**
* Randompage shuffles random viedeopreviews and provides a shuffle btn * Randompage shuffles random viedeopreviews and provides a shuffle btn
@ -81,7 +81,7 @@ class RandomPage extends React.Component {
}); });
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import RandomPage from "./RandomPage"; import RandomPage from './RandomPage';
describe('<RandomPage/>', function () { describe('<RandomPage/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -28,7 +28,7 @@ describe('<RandomPage/>', function () {
] ]
}); });
wrapper.find(".btnshuffle").simulate("click"); wrapper.find('.btnshuffle').simulate('click');
expect(global.fetch).toBeCalledTimes(2); expect(global.fetch).toBeCalledTimes(2);
}); });
@ -38,11 +38,11 @@ describe('<RandomPage/>', function () {
wrapper.setState({ wrapper.setState({
tags: [ tags: [
{tag_name: "test1"}, {tag_name: 'test1'},
{tag_name: "test2"} {tag_name: 'test2'}
] ]
}); });
expect(wrapper.find("Tag")).toHaveLength(2); expect(wrapper.find('Tag')).toHaveLength(2);
}); });
}); });

View File

@ -1,10 +1,10 @@
import React from "react"; import React from 'react';
import {Button, Col, Form} from "react-bootstrap"; import {Button, Col, Form} from 'react-bootstrap';
import style from "./GeneralSettings.module.css" import style from './GeneralSettings.module.css';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
import InfoHeaderItem from "../../elements/InfoHeaderItem/InfoHeaderItem"; import InfoHeaderItem from '../../elements/InfoHeaderItem/InfoHeaderItem';
import {faArchive, faBalanceScaleLeft, faRulerVertical} from "@fortawesome/free-solid-svg-icons"; import {faArchive, faBalanceScaleLeft, faRulerVertical} from '@fortawesome/free-solid-svg-icons';
import {faAddressCard} from "@fortawesome/free-regular-svg-icons"; import {faAddressCard} from '@fortawesome/free-regular-svg-icons';
/** /**
* Component for Generalsettings tag on Settingspage * Component for Generalsettings tag on Settingspage
@ -18,10 +18,10 @@ class GeneralSettings extends React.Component {
passwordsupport: false, passwordsupport: false,
tmdbsupport: null, tmdbsupport: null,
videopath: "", videopath: '',
tvshowpath: "", tvshowpath: '',
mediacentername: "", mediacentername: '',
password: "", password: '',
videonr: null, videonr: null,
dbsize: null, dbsize: null,
@ -44,7 +44,7 @@ class GeneralSettings extends React.Component {
subtext='Videos in Gravity' subtext='Videos in Gravity'
icon={faArchive}/> icon={faArchive}/>
<InfoHeaderItem backColor='yellow' <InfoHeaderItem backColor='yellow'
text={this.state.dbsize !== undefined ? this.state.dbsize + " MB" : undefined} text={this.state.dbsize !== undefined ? this.state.dbsize + ' MB' : undefined}
subtext='Database size' subtext='Database size'
icon={faRulerVertical}/> icon={faRulerVertical}/>
<InfoHeaderItem backColor='green' <InfoHeaderItem backColor='green'
@ -83,7 +83,7 @@ class GeneralSettings extends React.Component {
label='Enable Password support' label='Enable Password support'
checked={this.state.passwordsupport} checked={this.state.passwordsupport}
onChange={() => { onChange={() => {
this.setState({passwordsupport: !this.state.passwordsupport}) this.setState({passwordsupport: !this.state.passwordsupport});
}} }}
/> />
@ -102,7 +102,7 @@ class GeneralSettings extends React.Component {
label='Enable TMDB video grabbing support' label='Enable TMDB video grabbing support'
checked={this.state.tmdbsupport} checked={this.state.tmdbsupport}
onChange={() => { onChange={() => {
this.setState({tmdbsupport: !this.state.tmdbsupport}) this.setState({tmdbsupport: !this.state.tmdbsupport});
}} }}
/> />
@ -168,21 +168,21 @@ class GeneralSettings extends React.Component {
const updateRequest = new FormData(); const updateRequest = new FormData();
updateRequest.append('action', 'saveGeneralSettings'); updateRequest.append('action', 'saveGeneralSettings');
updateRequest.append('password', this.state.passwordsupport ? this.state.password : "-1"); updateRequest.append('password', this.state.passwordsupport ? this.state.password : '-1');
updateRequest.append('videopath', this.state.videopath); updateRequest.append('videopath', this.state.videopath);
updateRequest.append('tvshowpath', this.state.tvshowpath); updateRequest.append('tvshowpath', this.state.tvshowpath);
updateRequest.append('mediacentername', this.state.mediacentername); updateRequest.append('mediacentername', this.state.mediacentername);
updateRequest.append("tmdbsupport", this.state.tmdbsupport); updateRequest.append('tmdbsupport', this.state.tmdbsupport);
updateRequest.append("darkmodeenabled", GlobalInfos.isDarkTheme().toString()); updateRequest.append('darkmodeenabled', GlobalInfos.isDarkTheme().toString());
fetch('/api/settings.php', {method: 'POST', body: updateRequest}) fetch('/api/settings.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
if (result.success) { if (result.success) {
console.log("successfully saved settings"); console.log('successfully saved settings');
// todo 2020-07-10: popup success // todo 2020-07-10: popup success
} else { } else {
console.log("failed to save settings"); console.log('failed to save settings');
// todo 2020-07-10: popup error // todo 2020-07-10: popup error
} }
})); }));

View File

@ -1,7 +1,7 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import GeneralSettings from "./GeneralSettings"; import GeneralSettings from './GeneralSettings';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
describe('<GeneralSettings/>', function () { describe('<GeneralSettings/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -12,10 +12,10 @@ describe('<GeneralSettings/>', function () {
it('test password hide/show switchbutton', function () { it('test password hide/show switchbutton', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(0); expect(wrapper.find('[data-testid=\'passwordfield\']')).toHaveLength(0);
wrapper.find("FormCheck").findWhere(it => it.props().label === "Enable Password support").simulate("change"); wrapper.find('FormCheck').findWhere(it => it.props().label === 'Enable Password support').simulate('change');
expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(1); expect(wrapper.find('[data-testid=\'passwordfield\']')).toHaveLength(1);
}); });
it('test theme switchbutton', function () { it('test theme switchbutton', function () {
@ -23,7 +23,7 @@ describe('<GeneralSettings/>', function () {
GlobalInfos.enableDarkTheme(false); GlobalInfos.enableDarkTheme(false);
expect(GlobalInfos.isDarkTheme()).toBe(false); expect(GlobalInfos.isDarkTheme()).toBe(false);
wrapper.find("[data-testid='darktheme-switch']").simulate("change"); wrapper.find('[data-testid=\'darktheme-switch\']').simulate('change');
expect(GlobalInfos.isDarkTheme()).toBe(true); expect(GlobalInfos.isDarkTheme()).toBe(true);
}); });
@ -34,7 +34,7 @@ describe('<GeneralSettings/>', function () {
expect(global.fetch).toBeCalledTimes(0); expect(global.fetch).toBeCalledTimes(0);
const fakeEvent = {preventDefault: () => console.log('preventDefault')}; const fakeEvent = {preventDefault: () => console.log('preventDefault')};
wrapper.find("[data-testid='mainformsettings']").simulate("submit", fakeEvent); wrapper.find('[data-testid=\'mainformsettings\']').simulate('submit', fakeEvent);
expect(global.fetch).toBeCalledTimes(1); expect(global.fetch).toBeCalledTimes(1);
process.nextTick(() => { process.nextTick(() => {
@ -52,7 +52,7 @@ describe('<GeneralSettings/>', function () {
expect(global.fetch).toBeCalledTimes(0); expect(global.fetch).toBeCalledTimes(0);
const fakeEvent = {preventDefault: () => console.log('preventDefault')}; const fakeEvent = {preventDefault: () => console.log('preventDefault')};
wrapper.find("[data-testid='mainformsettings']").simulate("submit", fakeEvent); wrapper.find('[data-testid=\'mainformsettings\']').simulate('submit', fakeEvent);
expect(global.fetch).toBeCalledTimes(1); expect(global.fetch).toBeCalledTimes(1);
process.nextTick(() => { process.nextTick(() => {
@ -66,39 +66,39 @@ describe('<GeneralSettings/>', function () {
it('test videopath change event', function () { it('test videopath change event', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
expect(wrapper.state().videopath).not.toBe("test"); expect(wrapper.state().videopath).not.toBe('test');
const event = {target: {name: "pollName", value: "test"}}; const event = {target: {name: 'pollName', value: 'test'}};
wrapper.find("[data-testid='videpathform']").find("FormControl").simulate("change", event); wrapper.find('[data-testid=\'videpathform\']').find('FormControl').simulate('change', event);
expect(wrapper.state().videopath).toBe("test"); expect(wrapper.state().videopath).toBe('test');
}); });
it('test tvshowpath change event', function () { it('test tvshowpath change event', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
const event = {target: {name: "pollName", value: "test"}}; const event = {target: {name: 'pollName', value: 'test'}};
expect(wrapper.state().tvshowpath).not.toBe("test"); expect(wrapper.state().tvshowpath).not.toBe('test');
wrapper.find("[data-testid='tvshowpath']").find("FormControl").simulate("change", event); wrapper.find('[data-testid=\'tvshowpath\']').find('FormControl').simulate('change', event);
expect(wrapper.state().tvshowpath).toBe("test"); expect(wrapper.state().tvshowpath).toBe('test');
}); });
it('test mediacentername-form change event', function () { it('test mediacentername-form change event', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
const event = {target: {name: "pollName", value: "test"}}; const event = {target: {name: 'pollName', value: 'test'}};
expect(wrapper.state().mediacentername).not.toBe("test"); expect(wrapper.state().mediacentername).not.toBe('test');
wrapper.find("[data-testid='nameform']").find("FormControl").simulate("change", event); wrapper.find('[data-testid=\'nameform\']').find('FormControl').simulate('change', event);
expect(wrapper.state().mediacentername).toBe("test"); expect(wrapper.state().mediacentername).toBe('test');
}); });
it('test password-form change event', function () { it('test password-form change event', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
wrapper.setState({passwordsupport: true}); wrapper.setState({passwordsupport: true});
const event = {target: {name: "pollName", value: "test"}}; const event = {target: {name: 'pollName', value: 'test'}};
expect(wrapper.state().password).not.toBe("test"); expect(wrapper.state().password).not.toBe('test');
wrapper.find("[data-testid='passwordfield']").find("FormControl").simulate("change", event); wrapper.find('[data-testid=\'passwordfield\']').find('FormControl').simulate('change', event);
expect(wrapper.state().password).toBe("test"); expect(wrapper.state().password).toBe('test');
}); });
it('test tmdbsupport change event', function () { it('test tmdbsupport change event', function () {
@ -106,12 +106,12 @@ describe('<GeneralSettings/>', function () {
wrapper.setState({tmdbsupport: true}); wrapper.setState({tmdbsupport: true});
expect(wrapper.state().tmdbsupport).toBe(true); expect(wrapper.state().tmdbsupport).toBe(true);
wrapper.find("[data-testid='tmdb-switch']").simulate("change"); wrapper.find('[data-testid=\'tmdb-switch\']').simulate('change');
expect(wrapper.state().tmdbsupport).toBe(false); expect(wrapper.state().tmdbsupport).toBe(false);
}); });
it('test insertion of 4 infoheaderitems', function () { it('test insertion of 4 infoheaderitems', function () {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);
expect(wrapper.find("InfoHeaderItem").length).toBe(4); expect(wrapper.find('InfoHeaderItem').length).toBe(4);
}); });
}); });

View File

@ -1,5 +1,5 @@
import React from "react"; import React from 'react';
import style from "./MovieSettings.module.css" import style from './MovieSettings.module.css';
/** /**
* Component for MovieSettings on Settingspage * Component for MovieSettings on Settingspage
@ -28,10 +28,10 @@ class MovieSettings extends React.Component {
<> <>
<button disabled={this.state.startbtnDisabled} <button disabled={this.state.startbtnDisabled}
className='btn btn-success' className='btn btn-success'
onClick={() => {this.startReindex()}}>Reindex Movie onClick={() => {this.startReindex();}}>Reindex Movie
</button> </button>
<button className='btn btn-warning' <button className='btn btn-warning'
onClick={() => {this.cleanupGravity()}}>Cleanup Gravity onClick={() => {this.cleanupGravity();}}>Cleanup Gravity
</button> </button>
<div className={style.indextextarea}>{this.state.text.map(m => ( <div className={style.indextextarea}>{this.state.text.map(m => (
<div className='textarea-element'>{m}</div> <div className='textarea-element'>{m}</div>
@ -49,23 +49,23 @@ class MovieSettings extends React.Component {
this.setState({startbtnDisabled: true}); this.setState({startbtnDisabled: true});
console.log("starting"); console.log('starting');
const request = new FormData(); const request = new FormData();
request.append("action", "startReindex"); request.append('action', 'startReindex');
// fetch all videos available // fetch all videos available
fetch('/api/settings.php', {method: 'POST', body: request}) fetch('/api/settings.php', {method: 'POST', body: request})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
console.log(result); console.log(result);
if (result.success) { if (result.success) {
console.log("started successfully"); console.log('started successfully');
} else { } else {
console.log("error, reindex already running"); console.log('error, reindex already running');
this.setState({startbtnDisabled: true}); this.setState({startbtnDisabled: true});
} }
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
if (this.myinterval) { if (this.myinterval) {
clearInterval(this.myinterval); clearInterval(this.myinterval);
@ -78,7 +78,7 @@ class MovieSettings extends React.Component {
*/ */
updateStatus = () => { updateStatus = () => {
const request = new FormData(); const request = new FormData();
request.append("action", "getStatusMessage"); request.append('action', 'getStatusMessage');
fetch('/api/settings.php', {method: 'POST', body: request}) fetch('/api/settings.php', {method: 'POST', body: request})
.then((response) => response.json() .then((response) => response.json()
@ -88,7 +88,7 @@ class MovieSettings extends React.Component {
// todo 2020-07-4: scroll to bottom of div here // todo 2020-07-4: scroll to bottom of div here
this.setState({ this.setState({
// insert a string for each line // insert a string for each line
text: [...result.message.split("\n"), text: [...result.message.split('\n'),
...this.state.text] ...this.state.text]
}); });
} else { } else {
@ -99,7 +99,7 @@ class MovieSettings extends React.Component {
} }
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
}; };
@ -108,7 +108,7 @@ class MovieSettings extends React.Component {
*/ */
cleanupGravity() { cleanupGravity() {
const request = new FormData(); const request = new FormData();
request.append("action", "cleanupGravity"); request.append('action', 'cleanupGravity');
fetch('/api/settings.php', {method: 'POST', body: request}) fetch('/api/settings.php', {method: 'POST', body: request})
.then((response) => response.text() .then((response) => response.text()
@ -118,7 +118,7 @@ class MovieSettings extends React.Component {
}); });
})) }))
.catch(() => { .catch(() => {
console.log("no connection to backend"); console.log('no connection to backend');
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import MovieSettings from "./MovieSettings"; import MovieSettings from './MovieSettings';
describe('<MovieSettings/>', function () { describe('<MovieSettings/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -13,19 +13,19 @@ describe('<MovieSettings/>', function () {
wrapper.setState({ wrapper.setState({
text: [ text: [
"firstline", 'firstline',
"secline" 'secline'
] ]
}); });
expect(wrapper.find(".indextextarea").find(".textarea-element")).toHaveLength(2); expect(wrapper.find('.indextextarea').find('.textarea-element')).toHaveLength(2);
}); });
it('test simulate reindex', function () { it('test simulate reindex', function () {
global.fetch = global.prepareFetchApi({success: true}); global.fetch = global.prepareFetchApi({success: true});
const wrapper = shallow(<MovieSettings/>); const wrapper = shallow(<MovieSettings/>);
wrapper.find("button").findWhere(e => e.text() === "Reindex Movie" && e.type() === "button").simulate("click"); wrapper.find('button').findWhere(e => e.text() === 'Reindex Movie' && e.type() === 'button').simulate('click');
// initial send of reindex request to server // initial send of reindex request to server
expect(global.fetch).toBeCalledTimes(1); expect(global.fetch).toBeCalledTimes(1);
@ -35,7 +35,7 @@ describe('<MovieSettings/>', function () {
global.fetch = global.prepareFetchApi({success: false}); global.fetch = global.prepareFetchApi({success: false});
const wrapper = shallow(<MovieSettings/>); const wrapper = shallow(<MovieSettings/>);
wrapper.find("button").findWhere(e => e.text() === "Reindex Movie" && e.type() === "button").simulate("click"); wrapper.find('button').findWhere(e => e.text() === 'Reindex Movie' && e.type() === 'button').simulate('click');
// initial send of reindex request to server // initial send of reindex request to server
expect(global.fetch).toBeCalledTimes(1); expect(global.fetch).toBeCalledTimes(1);
@ -52,7 +52,7 @@ describe('<MovieSettings/>', function () {
it('content available received and in state', done => { it('content available received and in state', done => {
global.fetch = global.prepareFetchApi({ global.fetch = global.prepareFetchApi({
contentAvailable: true, contentAvailable: true,
message: "firstline\nsecondline" message: 'firstline\nsecondline'
}); });
const wrapper = shallow(<MovieSettings/>); const wrapper = shallow(<MovieSettings/>);
wrapper.instance().updateStatus(); wrapper.instance().updateStatus();
@ -60,8 +60,8 @@ describe('<MovieSettings/>', function () {
process.nextTick(() => { process.nextTick(() => {
expect(wrapper.state()).toMatchObject({ expect(wrapper.state()).toMatchObject({
text: [ text: [
"firstline", 'firstline',
"secondline" 'secondline'
] ]
}); });
@ -93,11 +93,11 @@ describe('<MovieSettings/>', function () {
}); });
it('test simulate gravity cleanup', done => { it('test simulate gravity cleanup', done => {
global.fetch = global.prepareFetchApi("mmi"); global.fetch = global.prepareFetchApi('mmi');
const wrapper = shallow(<MovieSettings/>); const wrapper = shallow(<MovieSettings/>);
wrapper.instance().setState = jest.fn(), wrapper.instance().setState = jest.fn(),
wrapper.find("button").findWhere(e => e.text() === "Cleanup Gravity" && e.type() === "button").simulate("click"); wrapper.find('button').findWhere(e => e.text() === 'Cleanup Gravity' && e.type() === 'button').simulate('click');
// initial send of reindex request to server // initial send of reindex request to server
expect(global.fetch).toBeCalledTimes(1); expect(global.fetch).toBeCalledTimes(1);

View File

@ -1,8 +1,8 @@
import React from "react"; import React from 'react';
import MovieSettings from "./MovieSettings"; import MovieSettings from './MovieSettings';
import GeneralSettings from "./GeneralSettings"; import GeneralSettings from './GeneralSettings';
import style from "./SettingsPage.module.css" import style from './SettingsPage.module.css';
import GlobalInfos from "../../GlobalInfos"; import GlobalInfos from '../../GlobalInfos';
/** /**
* The Settingspage handles all kinds of settings for the mediacenter * The Settingspage handles all kinds of settings for the mediacenter
@ -13,7 +13,7 @@ class SettingsPage extends React.Component {
super(props, context); super(props, context);
this.state = { this.state = {
currentpage: "general" currentpage: 'general'
}; };
} }
@ -23,14 +23,14 @@ class SettingsPage extends React.Component {
*/ */
getContent() { getContent() {
switch (this.state.currentpage) { switch (this.state.currentpage) {
case "general": case 'general':
return <GeneralSettings/>; return <GeneralSettings/>;
case "movies": case 'movies':
return <MovieSettings/>; return <MovieSettings/>;
case "tv": case 'tv':
return <span/>; // todo this page return <span/>; // todo this page
default: default:
return "unknown button clicked"; return 'unknown button clicked';
} }
} }
@ -40,13 +40,13 @@ class SettingsPage extends React.Component {
<div> <div>
<div className={style.SettingsSidebar + ' ' + themestyle.secbackground}> <div className={style.SettingsSidebar + ' ' + themestyle.secbackground}>
<div className={style.SettingsSidebarTitle + ' ' + themestyle.lighttextcolor}>Settings</div> <div className={style.SettingsSidebarTitle + ' ' + themestyle.lighttextcolor}>Settings</div>
<div onClick={() => this.setState({currentpage: "general"})} <div onClick={() => this.setState({currentpage: 'general'})}
className={style.SettingSidebarElement}>General className={style.SettingSidebarElement}>General
</div> </div>
<div onClick={() => this.setState({currentpage: "movies"})} <div onClick={() => this.setState({currentpage: 'movies'})}
className={style.SettingSidebarElement}>Movies className={style.SettingSidebarElement}>Movies
</div> </div>
<div onClick={() => this.setState({currentpage: "tv"})} <div onClick={() => this.setState({currentpage: 'tv'})}
className={style.SettingSidebarElement}>TV Shows className={style.SettingSidebarElement}>TV Shows
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme"; import {shallow} from 'enzyme';
import React from "react"; import React from 'react';
import SettingsPage from "./SettingsPage"; import SettingsPage from './SettingsPage';
describe('<RandomPage/>', function () { describe('<RandomPage/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -11,30 +11,30 @@ describe('<RandomPage/>', function () {
it('simulate topic clicka', function () { it('simulate topic clicka', function () {
const wrapper = shallow(<SettingsPage/>); const wrapper = shallow(<SettingsPage/>);
simulateSideBarClick("General", wrapper); simulateSideBarClick('General', wrapper);
expect(wrapper.state().currentpage).toBe("general"); expect(wrapper.state().currentpage).toBe('general');
expect(wrapper.find(".SettingsContent").find("GeneralSettings")).toHaveLength(1); expect(wrapper.find('.SettingsContent').find('GeneralSettings')).toHaveLength(1);
simulateSideBarClick("Movies", wrapper); simulateSideBarClick('Movies', wrapper);
expect(wrapper.state().currentpage).toBe("movies"); expect(wrapper.state().currentpage).toBe('movies');
expect(wrapper.find(".SettingsContent").find("MovieSettings")).toHaveLength(1); expect(wrapper.find('.SettingsContent').find('MovieSettings')).toHaveLength(1);
simulateSideBarClick("TV Shows", wrapper); simulateSideBarClick('TV Shows', wrapper);
expect(wrapper.state().currentpage).toBe("tv"); expect(wrapper.state().currentpage).toBe('tv');
expect(wrapper.find(".SettingsContent").find("span")).toHaveLength(1); expect(wrapper.find('.SettingsContent').find('span')).toHaveLength(1);
}); });
function simulateSideBarClick(name, wrapper) { function simulateSideBarClick(name, wrapper) {
wrapper.find(".SettingSidebarElement").findWhere(it => wrapper.find('.SettingSidebarElement').findWhere(it =>
it.text() === name && it.text() === name &&
it.type() === "div").simulate("click"); it.type() === 'div').simulate('click');
} }
it('simulate unknown topic', function () { it('simulate unknown topic', function () {
const wrapper = shallow(<SettingsPage/>); const wrapper = shallow(<SettingsPage/>);
wrapper.setState({currentpage: "unknown"}); wrapper.setState({currentpage: 'unknown'});
expect(wrapper.find(".SettingsContent").text()).toBe("unknown button clicked"); expect(wrapper.find('.SettingsContent').text()).toBe('unknown button clicked');
}); });
}); });

View File

@ -21,13 +21,13 @@ global.prepareFetchApi = (response) => {
text: () => mockJsonPromise text: () => mockJsonPromise
}); });
return (jest.fn().mockImplementation(() => mockFetchPromise)); return (jest.fn().mockImplementation(() => mockFetchPromise));
} };
/** /**
* prepares a failing virtual fetch api call * prepares a failing virtual fetch api call
* @returns {jest.Mock<any, any>} a jest moch function simulating a failing fetch call * @returns {jest.Mock<any, any>} a jest moch function simulating a failing fetch call
*/ */
global.prepareFailingFetchApi = () => { global.prepareFailingFetchApi = () => {
const mockFetchPromise = Promise.reject("myreason"); const mockFetchPromise = Promise.reject('myreason');
return (jest.fn().mockImplementation(() => mockFetchPromise)); return (jest.fn().mockImplementation(() => mockFetchPromise));
} };