Merge branch 'SettingsPage' into 'master'
Settings page See merge request lukas/openmediacenter!6
This commit is contained in:
commit
ca499fed99
39
api/SSettings.php
Normal file
39
api/SSettings.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
class SSettings
|
||||
{
|
||||
private ?Database $database;
|
||||
|
||||
/**
|
||||
* SSettings constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->database = Database::getInstance();
|
||||
}
|
||||
|
||||
public function getVideoPath() {
|
||||
$query = "SELECT video_path from settings";
|
||||
|
||||
$result = $this->database->getConnection()->query($query);
|
||||
|
||||
$r = mysqli_fetch_assoc($result);
|
||||
return $r['video_path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* check if TMDB is enableds
|
||||
* @return bool isenabled?
|
||||
*/
|
||||
public function isTMDBGrabbingEnabled(): bool
|
||||
{
|
||||
$query = "SELECT TMDB_grabbing from settings";
|
||||
|
||||
$result = $this->database->getConnection()->query($query);
|
||||
if(!$result){
|
||||
return true; // if undefined in db --> default true
|
||||
}else{
|
||||
$r = mysqli_fetch_assoc($result);
|
||||
return $r['TMDB_grabbing'] == '1';
|
||||
}
|
||||
}
|
||||
}
|
65
api/Settings.php
Normal file
65
api/Settings.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
require 'Database.php';
|
||||
require 'SSettings.php';
|
||||
|
||||
$conn = Database::getInstance()->getConnection();
|
||||
$settings = new SSettings();
|
||||
|
||||
if (isset($_POST['action'])) {
|
||||
$action = $_POST['action'];
|
||||
switch ($action) {
|
||||
case "loadGeneralSettings":
|
||||
$query = "SELECT * from settings";
|
||||
|
||||
$result = $conn->query($query);
|
||||
if ($result->num_rows > 1) {
|
||||
// todo throw error
|
||||
}
|
||||
|
||||
$r = mysqli_fetch_assoc($result);
|
||||
// booleans need to be set manually
|
||||
$r['passwordEnabled'] = $r['password'] != "-1";
|
||||
$r['TMDB_grabbing'] = ($r['TMDB_grabbing'] != '0');
|
||||
|
||||
echo json_encode($r);
|
||||
break;
|
||||
case "saveGeneralSettings":
|
||||
$mediacentername = $_POST['mediacentername'];
|
||||
$password = $_POST['password'];
|
||||
$videopath = $_POST['videopath'];
|
||||
$tvshowpath = $_POST['tvshowpath'];
|
||||
$tmdbsupport = $_POST['tmdbsupport'];
|
||||
|
||||
$query = "UPDATE settings SET
|
||||
video_path='$videopath',
|
||||
episode_path='$tvshowpath',
|
||||
password='$password',
|
||||
mediacenter_name='$mediacentername',
|
||||
TMDB_grabbing=$tmdbsupport
|
||||
WHERE 1";
|
||||
|
||||
if ($conn->query($query) === true) {
|
||||
echo '{"success": true}';
|
||||
} else {
|
||||
echo '{"success": true}';
|
||||
}
|
||||
break;
|
||||
case "loadInitialData":
|
||||
$query = "SELECT * from settings";
|
||||
|
||||
$result = $conn->query($query);
|
||||
if ($result->num_rows > 1) {
|
||||
// todo throw error
|
||||
}
|
||||
|
||||
$r = mysqli_fetch_assoc($result);
|
||||
if ($r['password'] != "-1") {
|
||||
$r['passwordEnabled'] = true;
|
||||
} else {
|
||||
$r['passwordEnabled'] = false;
|
||||
}
|
||||
unset($r['password']);
|
||||
echo json_encode($r);
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require 'Database.php';
|
||||
require 'TMDBMovie.php';
|
||||
require 'SSettings.php';
|
||||
|
||||
writeLog("starting extraction!\n");
|
||||
|
||||
@ -10,9 +11,12 @@ $tmdb = new TMDBMovie();
|
||||
$tmdbgenres = $tmdb->getAllGenres();
|
||||
|
||||
$conn = Database::getInstance()->getConnection();
|
||||
$settings = new SSettings();
|
||||
|
||||
$scandir = "../videos/prn/";
|
||||
// load video path from settings
|
||||
$scandir = "../" . $settings->getVideoPath();
|
||||
$arr = scandir($scandir);
|
||||
$TMDB_enabled = $settings->isTMDBGrabbingEnabled();
|
||||
|
||||
$all = 0;
|
||||
$added = 0;
|
||||
@ -33,14 +37,21 @@ foreach ($arr as $elem) {
|
||||
$poster = -1;
|
||||
$genres = -1;
|
||||
if (!is_null($dta = $tmdb->searchMovie($moviename))) {
|
||||
$pic = file_get_contents($tmdb->picturebase . $dta->poster_path);
|
||||
$poster = shell_exec("ffmpeg -hide_banner -loglevel panic -ss 00:04:00 -i \"../videos/prn/$elem\" -vframes 1 -q:v 2 -f singlejpeg pipe:1 2>/dev/null");
|
||||
|
||||
// error handling for download error
|
||||
if (!$pic) {
|
||||
// check if tmdb support is enabled
|
||||
if ($TMDB_enabled) {
|
||||
$pic = file_get_contents($tmdb->picturebase . $dta->poster_path);
|
||||
|
||||
// error handling for download error
|
||||
if (!$pic) {
|
||||
$pic = $poster;
|
||||
$poster = -1;
|
||||
echo "Failed to load Picture from TMDB! \n";
|
||||
}
|
||||
} else {
|
||||
$pic = $poster;
|
||||
$poster = -1;
|
||||
echo "Failed to load Picture from TMDB! \n";
|
||||
}
|
||||
|
||||
$genres = $dta->genre_ids;
|
||||
|
@ -1,9 +1,15 @@
|
||||
<?php
|
||||
require 'Database.php';
|
||||
require 'SSettings.php';
|
||||
|
||||
// establish initial db connection
|
||||
$conn = Database::getInstance()->getConnection();
|
||||
$settings = new SSettings();
|
||||
|
||||
// load video path from settings
|
||||
$videopath = $settings->getVideoPath();
|
||||
|
||||
|
||||
//$_POST['action'] = "getRandomMovies";$_POST['number'] =6;
|
||||
if (isset($_POST['action'])) {
|
||||
$action = $_POST['action'];
|
||||
switch ($action) {
|
||||
@ -84,7 +90,9 @@ if (isset($_POST['action'])) {
|
||||
|
||||
$arr["movie_id"] = $row["movie_id"];
|
||||
$arr["movie_name"] = $row["movie_name"];
|
||||
$arr["movie_url"] = str_replace("?","%3F",$row["movie_url"]);
|
||||
// todo drop video url from db -- maybe one with and one without extension
|
||||
// extension hardcoded here!!!
|
||||
$arr["movie_url"] = str_replace("?", "%3F", $videopath . $row["movie_name"] . ".mp4");
|
||||
$arr["likes"] = $row["likes"];
|
||||
$arr["quality"] = $row["quality"];
|
||||
$arr["length"] = $row["length"];
|
||||
|
13
database.sql
13
database.sql
@ -29,9 +29,22 @@ create table if not exists video_tags
|
||||
foreign key (video_id) references videos (movie_id)
|
||||
);
|
||||
|
||||
create table settings
|
||||
(
|
||||
id enum(1) NOT NULL default 0,
|
||||
video_path varchar(255) null,
|
||||
episode_path varchar(255) null,
|
||||
password varchar(32) default '-1' null,
|
||||
mediacenter_name varchar(32) default 'OpenMediaCenter' null,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
INSERT INTO tags (tag_id, tag_name)
|
||||
VALUES (2, 'fullhd');
|
||||
INSERT INTO tags (tag_id, tag_name)
|
||||
VALUES (3, 'lowquality');
|
||||
INSERT INTO tags (tag_id, tag_name)
|
||||
VALUES (4, 'hd');
|
||||
|
||||
INSERT INTO settings (video_path, episode_path, password, mediacenter_name)
|
||||
VALUES ('./videos/', './tvshows/', -1, 'OpenMediaCenter');
|
||||
|
50
src/App.js
50
src/App.js
@ -11,37 +11,57 @@ import CategoryPage from "./pages/CategoryPage/CategoryPage";
|
||||
class App extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {page: "default"};
|
||||
this.state = {
|
||||
page: "default",
|
||||
generalSettingsLoaded: false,
|
||||
passwordsupport: null,
|
||||
mediacentername: "OpenMediaCenter"
|
||||
};
|
||||
|
||||
// bind this to the method for being able to call methods such as this.setstate
|
||||
this.changeRootElement = this.changeRootElement.bind(this);
|
||||
this.returnToLastElement = this.returnToLastElement.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const updateRequest = new FormData();
|
||||
updateRequest.append('action', 'loadInitialData');
|
||||
|
||||
fetch('/api/Settings.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
console.log(result);
|
||||
this.setState({
|
||||
generalSettingsLoaded: true,
|
||||
passwordsupport: result.passwordEnabled,
|
||||
mediacentername: result.mediacenter_name
|
||||
});
|
||||
console.log(this.state);
|
||||
}));
|
||||
}
|
||||
|
||||
newElement = null;
|
||||
|
||||
constructViewBinding() {
|
||||
return {
|
||||
changeRootElement: this.changeRootElement,
|
||||
returnToLastElement: this.returnToLastElement
|
||||
};
|
||||
}
|
||||
|
||||
MainBody() {
|
||||
let page;
|
||||
if (this.state.page === "default") {
|
||||
page = <HomePage viewbinding={{
|
||||
changeRootElement: this.changeRootElement,
|
||||
returnToLastElement: this.returnToLastElement
|
||||
}}/>;
|
||||
page = <HomePage viewbinding={this.constructViewBinding()}/>;
|
||||
this.mypage = page;
|
||||
} else if (this.state.page === "random") {
|
||||
page = <RandomPage viewbinding={{
|
||||
changeRootElement: this.changeRootElement,
|
||||
returnToLastElement: this.returnToLastElement
|
||||
}}/>;
|
||||
page = <RandomPage viewbinding={this.constructViewBinding()}/>;
|
||||
this.mypage = page;
|
||||
} else if (this.state.page === "settings") {
|
||||
page = <SettingsPage/>;
|
||||
this.mypage = page;
|
||||
} else if (this.state.page === "categories") {
|
||||
page = <CategoryPage viewbinding={{
|
||||
changeRootElement: this.changeRootElement,
|
||||
returnToLastElement: this.returnToLastElement
|
||||
}}/>;
|
||||
page = <CategoryPage viewbinding={this.constructViewBinding()}/>;
|
||||
this.mypage = page;
|
||||
} else if (this.state.page === "video") {
|
||||
// show videoelement if neccessary
|
||||
@ -61,7 +81,7 @@ class App extends React.Component {
|
||||
return (
|
||||
<div className="App">
|
||||
<nav className="navbar navbar-expand-sm bg-primary navbar-dark">
|
||||
<div className="navbar-brand">OpenMediaCenter</div>
|
||||
<div className="navbar-brand">{this.state.mediacentername}</div>
|
||||
|
||||
<ul className="navbar-nav">
|
||||
<li className="nav-item">
|
||||
@ -90,7 +110,7 @@ class App extends React.Component {
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{this.MainBody()}
|
||||
{this.state.generalSettingsLoaded ? this.MainBody() : "loading"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,14 @@ import React from 'react';
|
||||
import App from './App';
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
function prepareFetchApi(response) {
|
||||
const mockJsonPromise = Promise.resolve(response);
|
||||
const mockFetchPromise = Promise.resolve({
|
||||
json: () => mockJsonPromise,
|
||||
});
|
||||
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||
}
|
||||
|
||||
describe('<App/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
@ -20,6 +28,7 @@ describe('<App/>', function () {
|
||||
|
||||
it('simulate video view change ', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
wrapper.instance().changeRootElement(<div id='testit'></div>);
|
||||
|
||||
@ -28,6 +37,7 @@ describe('<App/>', function () {
|
||||
|
||||
it('test hide video again', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
wrapper.instance().changeRootElement(<div id='testit'></div>);
|
||||
|
||||
@ -40,6 +50,7 @@ describe('<App/>', function () {
|
||||
|
||||
it('test fallback to last loaded page', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
wrapper.find(".nav-link").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click");
|
||||
|
||||
@ -54,6 +65,8 @@ describe('<App/>', function () {
|
||||
|
||||
it('test home click', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
wrapper.setState({page: "wrongvalue"});
|
||||
expect(wrapper.find("HomePage")).toHaveLength(0);
|
||||
wrapper.find(".nav-link").findWhere(t => t.text() === "Home" && t.type() === "div").simulate("click");
|
||||
@ -62,6 +75,7 @@ describe('<App/>', function () {
|
||||
|
||||
it('test category click', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
expect(wrapper.find("CategoryPage")).toHaveLength(0);
|
||||
wrapper.find(".nav-link").findWhere(t => t.text() === "Categories" && t.type() === "div").simulate("click");
|
||||
@ -70,9 +84,33 @@ describe('<App/>', function () {
|
||||
|
||||
it('test settings click', function () {
|
||||
const wrapper = shallow(<App/>);
|
||||
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
|
||||
|
||||
expect(wrapper.find("SettingsPage")).toHaveLength(0);
|
||||
wrapper.find(".nav-link").findWhere(t => t.text() === "Settings" && t.type() === "div").simulate("click");
|
||||
expect(wrapper.find("SettingsPage")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('test initial fetch from api', done => {
|
||||
global.fetch = prepareFetchApi({
|
||||
generalSettingsLoaded: true,
|
||||
passwordsupport: true,
|
||||
mediacentername: "testname"
|
||||
});
|
||||
|
||||
const wrapper = shallow(<App/>);
|
||||
|
||||
|
||||
const func = jest.fn();
|
||||
wrapper.instance().setState = func;
|
||||
|
||||
expect(global.fetch).toBeCalledTimes(1);
|
||||
|
||||
process.nextTick(() => {
|
||||
expect(func).toBeCalledTimes(1);
|
||||
|
||||
global.fetch.mockClear();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,10 +13,6 @@ class Preview extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.setState({});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
previewpicture: null,
|
||||
@ -30,9 +26,9 @@ class Preview extends React.Component {
|
||||
fetch('/api/videoload.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.text()
|
||||
.then((result) => {
|
||||
this.setState(prevState => ({
|
||||
...prevState.previewpicture, previewpicture: result
|
||||
}));
|
||||
this.setState({
|
||||
previewpicture: result
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {shallow, mount} from "enzyme";
|
||||
import {mount, shallow} from "enzyme";
|
||||
import React from "react";
|
||||
import CategoryPage from "./CategoryPage";
|
||||
import VideoContainer from "../../elements/VideoContainer/VideoContainer";
|
||||
|
||||
function prepareFetchApi(response) {
|
||||
const mockJsonPromise = Promise.resolve(response);
|
||||
@ -111,4 +110,21 @@ describe('<CategoryPage/>', function () {
|
||||
|
||||
expect(func).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('test sidebar tag clicks', function () {
|
||||
const func = jest.fn();
|
||||
|
||||
const wrapper = mount(<CategoryPage category="fullhd"/>);
|
||||
wrapper.instance().loadTag = func;
|
||||
|
||||
console.log(wrapper.debug());
|
||||
|
||||
expect(func).toBeCalledTimes(0);
|
||||
wrapper.find("SideBar").find("Tag").forEach(e => {
|
||||
e.simulate("click");
|
||||
})
|
||||
|
||||
expect(func).toBeCalledTimes(4);
|
||||
|
||||
});
|
||||
});
|
||||
|
131
src/pages/SettingsPage/GeneralSettings.js
Normal file
131
src/pages/SettingsPage/GeneralSettings.js
Normal file
@ -0,0 +1,131 @@
|
||||
import React from "react";
|
||||
import {Button, Col, Form} from "react-bootstrap";
|
||||
import style from "./GeneralSettings.module.css"
|
||||
|
||||
class GeneralSettings extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
passwordsupport: false,
|
||||
tmdbsupport: null,
|
||||
|
||||
videopath: "",
|
||||
tvshowpath: "",
|
||||
mediacentername: "",
|
||||
password: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const updateRequest = new FormData();
|
||||
updateRequest.append('action', 'loadGeneralSettings');
|
||||
|
||||
fetch('/api/Settings.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
console.log(result);
|
||||
this.setState({
|
||||
videopath: result.video_path,
|
||||
tvshowpath: result.episode_path,
|
||||
mediacentername: result.mediacenter_name,
|
||||
password: result.password,
|
||||
passwordsupport: result.passwordEnabled,
|
||||
tmdbsupport: result.TMDB_grabbing
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className={style.GeneralForm}>
|
||||
<Form data-testid='mainformsettings' onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
this.saveSettings();
|
||||
}}>
|
||||
<Form.Row>
|
||||
<Form.Group as={Col} data-testid="videpathform">
|
||||
<Form.Label>Video Path</Form.Label>
|
||||
<Form.Control type="text" placeholder="/var/www/html/video" value={this.state.videopath}
|
||||
onChange={(ee) => this.setState({videopath: ee.target.value})}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group as={Col} data-testid="tvshowpath">
|
||||
<Form.Label>TV Show Path</Form.Label>
|
||||
<Form.Control type='text' placeholder="/var/www/html/tvshow"
|
||||
value={this.state.tvshowpath}
|
||||
onChange={(e) => this.setState({tvshowpath: e.target.value})}/>
|
||||
</Form.Group>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Check
|
||||
type="switch"
|
||||
id="custom-switch"
|
||||
data-testid='passwordswitch'
|
||||
label="Enable Password support"
|
||||
checked={this.state.passwordsupport}
|
||||
onChange={() => {
|
||||
this.setState({passwordsupport: !this.state.passwordsupport})
|
||||
}}
|
||||
/>
|
||||
|
||||
<Form.Check
|
||||
type="switch"
|
||||
id="custom-switch-2"
|
||||
data-testid='tmdb-switch'
|
||||
label="Enable TMDB video grabbing support"
|
||||
checked={this.state.tmdbsupport}
|
||||
onChange={() => {
|
||||
this.setState({tmdbsupport: !this.state.tmdbsupport})
|
||||
}}
|
||||
/>
|
||||
|
||||
{this.state.passwordsupport ?
|
||||
<Form.Group data-testid="passwordfield">
|
||||
<Form.Label>Password</Form.Label>
|
||||
<Form.Control type="password" placeholder="**********" value={this.state.password}
|
||||
onChange={(e) => this.setState({password: e.target.value})}/>
|
||||
</Form.Group> : null
|
||||
}
|
||||
|
||||
<Form.Group className={style.mediacenternameform} data-testid="nameform">
|
||||
<Form.Label>The name of the Mediacenter</Form.Label>
|
||||
<Form.Control type="text" placeholder="Mediacentername" value={this.state.mediacentername}
|
||||
onChange={(e) => this.setState({mediacentername: e.target.value})}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
const updateRequest = new FormData();
|
||||
updateRequest.append('action', 'saveGeneralSettings');
|
||||
|
||||
updateRequest.append('password', this.state.passwordsupport ? this.state.password : "-1");
|
||||
updateRequest.append('videopath', this.state.videopath);
|
||||
updateRequest.append('tvshowpath', this.state.tvshowpath);
|
||||
updateRequest.append('mediacentername', this.state.mediacentername);
|
||||
updateRequest.append("tmdbsupport", this.state.tmdbsupport);
|
||||
|
||||
fetch('/api/Settings.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
console.log("successfully saved settings");
|
||||
// todo 2020-07-10: popup success
|
||||
} else {
|
||||
console.log("failed to save settings");
|
||||
// todo 2020-07-10: popup error
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export default GeneralSettings;
|
8
src/pages/SettingsPage/GeneralSettings.module.css
Normal file
8
src/pages/SettingsPage/GeneralSettings.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
.GeneralForm {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.mediacenternameform {
|
||||
margin-top: 25px;
|
||||
width: 40%;
|
||||
}
|
110
src/pages/SettingsPage/GeneralSettings.test.js
Normal file
110
src/pages/SettingsPage/GeneralSettings.test.js
Normal file
@ -0,0 +1,110 @@
|
||||
import {shallow} from "enzyme";
|
||||
import React from "react";
|
||||
import GeneralSettings from "./GeneralSettings";
|
||||
|
||||
function prepareFetchApi(response) {
|
||||
const mockJsonPromise = Promise.resolve(response);
|
||||
const mockFetchPromise = Promise.resolve({
|
||||
json: () => mockJsonPromise,
|
||||
});
|
||||
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||
}
|
||||
|
||||
describe('<GeneralSettings/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('test password hide/show switchbutton', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(0);
|
||||
wrapper.find("FormCheck").findWhere(it => it.props().label === "Enable Password support").simulate("change");
|
||||
|
||||
expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('test savesettings', done => {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
global.fetch = prepareFetchApi({success: true});
|
||||
|
||||
expect(global.fetch).toBeCalledTimes(0);
|
||||
const fakeEvent = {preventDefault: () => console.log('preventDefault')};
|
||||
wrapper.find("[data-testid='mainformsettings']").simulate("submit", fakeEvent);
|
||||
expect(global.fetch).toBeCalledTimes(1);
|
||||
|
||||
process.nextTick(() => {
|
||||
// todo 2020-07-13: test popup of error success here
|
||||
|
||||
global.fetch.mockClear();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test failing savesettings', done => {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
global.fetch = prepareFetchApi({success: false});
|
||||
|
||||
expect(global.fetch).toBeCalledTimes(0);
|
||||
const fakeEvent = {preventDefault: () => console.log('preventDefault')};
|
||||
wrapper.find("[data-testid='mainformsettings']").simulate("submit", fakeEvent);
|
||||
expect(global.fetch).toBeCalledTimes(1);
|
||||
|
||||
process.nextTick(() => {
|
||||
// todo 2020-07-13: test error popup here!
|
||||
|
||||
global.fetch.mockClear();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test videopath change event', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
expect(wrapper.state().videopath).not.toBe("test");
|
||||
|
||||
const event = {target: {name: "pollName", value: "test"}};
|
||||
wrapper.find("[data-testid='videpathform']").find("FormControl").simulate("change", event);
|
||||
expect(wrapper.state().videopath).toBe("test");
|
||||
});
|
||||
|
||||
it('test tvshowpath change event', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
const event = {target: {name: "pollName", value: "test"}};
|
||||
expect(wrapper.state().tvshowpath).not.toBe("test");
|
||||
wrapper.find("[data-testid='tvshowpath']").find("FormControl").simulate("change", event);
|
||||
expect(wrapper.state().tvshowpath).toBe("test");
|
||||
});
|
||||
|
||||
it('test mediacentername-form change event', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
|
||||
const event = {target: {name: "pollName", value: "test"}};
|
||||
expect(wrapper.state().mediacentername).not.toBe("test");
|
||||
wrapper.find("[data-testid='nameform']").find("FormControl").simulate("change", event);
|
||||
expect(wrapper.state().mediacentername).toBe("test");
|
||||
});
|
||||
|
||||
it('test password-form change event', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
wrapper.setState({passwordsupport: true});
|
||||
|
||||
const event = {target: {name: "pollName", value: "test"}};
|
||||
expect(wrapper.state().password).not.toBe("test");
|
||||
wrapper.find("[data-testid='passwordfield']").find("FormControl").simulate("change", event);
|
||||
expect(wrapper.state().password).toBe("test");
|
||||
});
|
||||
|
||||
it('test tmdbsupport change event', function () {
|
||||
const wrapper = shallow(<GeneralSettings/>);
|
||||
wrapper.setState({tmdbsupport: true});
|
||||
|
||||
expect(wrapper.state().tmdbsupport).toBe(true);
|
||||
wrapper.find("[data-testid='tmdb-switch']").simulate("change");
|
||||
expect(wrapper.state().tmdbsupport).toBe(false);
|
||||
});
|
||||
});
|
89
src/pages/SettingsPage/MovieSettings.js
Normal file
89
src/pages/SettingsPage/MovieSettings.js
Normal file
@ -0,0 +1,89 @@
|
||||
import React from "react";
|
||||
import style from "./MovieSettings.module.css"
|
||||
|
||||
class MovieSettings extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
text: [],
|
||||
startbtnDisabled: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.myinterval) {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
this.myinterval = setInterval(this.updateStatus, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<button disabled={this.state.startbtnDisabled} className='reindexbtn btn btn-success' onClick={() => {
|
||||
this.startReindex()
|
||||
}}>Reindex Movies
|
||||
</button>
|
||||
<div className={style.indextextarea}>{this.state.text.map(m => (
|
||||
<div className='textarea-element'>{m}</div>
|
||||
))}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
startReindex() {
|
||||
// clear output text before start
|
||||
this.setState({text: []});
|
||||
|
||||
this.setState({startbtnDisabled: true});
|
||||
|
||||
console.log("starting");
|
||||
const updateRequest = new FormData();
|
||||
// fetch all videos available
|
||||
fetch('/api/extractvideopreviews.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
// todo 2020-07-4: some kind of start event
|
||||
console.log("returned");
|
||||
}))
|
||||
.catch(() => {
|
||||
console.log("no connection to backend");
|
||||
});
|
||||
if (this.myinterval) {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
this.myinterval = setInterval(this.updateStatus, 1000);
|
||||
}
|
||||
|
||||
updateStatus = () => {
|
||||
const updateRequest = new FormData();
|
||||
fetch('/api/extractionData.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
if (result.contentAvailable === true) {
|
||||
console.log(result);
|
||||
// todo 2020-07-4: scroll to bottom of div here
|
||||
this.setState({
|
||||
// insert a string for each line
|
||||
text: [...result.message.split("\n"),
|
||||
...this.state.text]
|
||||
});
|
||||
} else {
|
||||
// clear refresh interval if no content available
|
||||
clearInterval(this.myinterval);
|
||||
|
||||
this.setState({startbtnDisabled: false});
|
||||
}
|
||||
}))
|
||||
.catch(() => {
|
||||
console.log("no connection to backend");
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default MovieSettings;
|
13
src/pages/SettingsPage/MovieSettings.module.css
Normal file
13
src/pages/SettingsPage/MovieSettings.module.css
Normal file
@ -0,0 +1,13 @@
|
||||
.indextextarea {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
|
||||
overflow-y: scroll;
|
||||
overflow-x: auto;
|
||||
|
||||
min-height: 100px;
|
||||
max-height: 300px;
|
||||
width: 50%;
|
||||
background-color: #c2c2c2;
|
||||
border-radius: 5px;
|
||||
}
|
62
src/pages/SettingsPage/MovieSettings.test.js
Normal file
62
src/pages/SettingsPage/MovieSettings.test.js
Normal file
@ -0,0 +1,62 @@
|
||||
import {shallow} from "enzyme";
|
||||
import React from "react";
|
||||
import MovieSettings from "./MovieSettings";
|
||||
|
||||
function prepareFetchApi(response) {
|
||||
const mockJsonPromise = Promise.resolve(response);
|
||||
const mockFetchPromise = Promise.resolve({
|
||||
json: () => mockJsonPromise,
|
||||
});
|
||||
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||
}
|
||||
|
||||
describe('<MovieSettings/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<MovieSettings/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('received text renders into dom', function () {
|
||||
const wrapper = shallow(<MovieSettings/>);
|
||||
|
||||
wrapper.setState({
|
||||
text: [
|
||||
"firstline",
|
||||
"secline"
|
||||
]
|
||||
});
|
||||
|
||||
expect(wrapper.find(".indextextarea").find(".textarea-element")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('test simulate reindex', function () {
|
||||
global.fetch = prepareFetchApi({});
|
||||
const wrapper = shallow(<MovieSettings/>);
|
||||
|
||||
wrapper.find(".reindexbtn").simulate("click");
|
||||
|
||||
// initial send of reindex request to server
|
||||
expect(global.fetch).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('content available received and in state', done => {
|
||||
global.fetch = prepareFetchApi({
|
||||
contentAvailable: true,
|
||||
message: "firstline\nsecondline"
|
||||
});
|
||||
const wrapper = shallow(<MovieSettings/>);
|
||||
wrapper.instance().updateStatus();
|
||||
|
||||
process.nextTick(() => {
|
||||
expect(wrapper.state()).toMatchObject({
|
||||
text: [
|
||||
"firstline",
|
||||
"secondline"
|
||||
]
|
||||
});
|
||||
|
||||
global.fetch.mockClear();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,82 +1,52 @@
|
||||
import React from "react";
|
||||
import PageTitle from "../../elements/PageTitle/PageTitle";
|
||||
import MovieSettings from "./MovieSettings";
|
||||
import GeneralSettings from "./GeneralSettings";
|
||||
import style from "./SettingsPage.module.css"
|
||||
|
||||
|
||||
class SettingsPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
text: []
|
||||
currentpage: "general"
|
||||
};
|
||||
}
|
||||
|
||||
updateStatus = () => {
|
||||
const updateRequest = new FormData();
|
||||
fetch('/api/extractionData.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
if (result.contentAvailable === true) {
|
||||
console.log(result);
|
||||
this.setState({
|
||||
text: [...result.message.split("\n"),
|
||||
...this.state.text]
|
||||
});
|
||||
} else {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
}))
|
||||
.catch(() => {
|
||||
console.log("no connection to backend");
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.myinterval) {
|
||||
clearInterval(this.myinterval);
|
||||
getContent() {
|
||||
switch (this.state.currentpage) {
|
||||
case "general":
|
||||
return <GeneralSettings/>;
|
||||
case "movies":
|
||||
return <MovieSettings/>;
|
||||
case "tv":
|
||||
return <span/>; // todo this page
|
||||
default:
|
||||
return "unknown button clicked";
|
||||
}
|
||||
this.myinterval = setInterval(this.updateStatus, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageTitle
|
||||
title='Settings Page'
|
||||
subtitle='todo'/>
|
||||
|
||||
<button className='reindexbtn btn btn-success' onClick={() => {
|
||||
this.startReindex()
|
||||
}}>Reindex Movies
|
||||
</button>
|
||||
<div className='indextextarea'>{this.state.text.map(m => (
|
||||
<div className='textarea-element'>{m}</div>
|
||||
))}</div>
|
||||
<div className={style.SettingsSidebar}>
|
||||
<div className={style.SettingsSidebarTitle}>Settings</div>
|
||||
<div onClick={() => this.setState({currentpage: "general"})}
|
||||
className={style.SettingSidebarElement}>General
|
||||
</div>
|
||||
<div onClick={() => this.setState({currentpage: "movies"})}
|
||||
className={style.SettingSidebarElement}>Movies
|
||||
</div>
|
||||
<div onClick={() => this.setState({currentpage: "tv"})}
|
||||
className={style.SettingSidebarElement}>TV Shows
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.SettingsContent}>
|
||||
{this.getContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
startReindex() {
|
||||
console.log("starting");
|
||||
const updateRequest = new FormData();
|
||||
// fetch all videos available
|
||||
fetch('/api/extractvideopreviews.php', {method: 'POST', body: updateRequest})
|
||||
.then((response) => response.json()
|
||||
.then((result) => {
|
||||
console.log("returned");
|
||||
}))
|
||||
.catch(() => {
|
||||
console.log("no connection to backend");
|
||||
});
|
||||
if (this.myinterval) {
|
||||
clearInterval(this.myinterval);
|
||||
}
|
||||
this.myinterval = setInterval(this.updateStatus, 1000);
|
||||
console.log("sent");
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsPage;
|
||||
|
40
src/pages/SettingsPage/SettingsPage.module.css
Normal file
40
src/pages/SettingsPage/SettingsPage.module.css
Normal file
@ -0,0 +1,40 @@
|
||||
.SettingsSidebar {
|
||||
padding-top: 20px;
|
||||
float: left;
|
||||
width: 10%;
|
||||
background-color: #d3dcef;
|
||||
min-height: calc(100vh - 56px);
|
||||
min-width: 110px;
|
||||
}
|
||||
|
||||
.SettingsSidebarTitle {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: larger;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.SettingsContent {
|
||||
float: left;
|
||||
width: 80%;
|
||||
padding-left: 30px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.SettingSidebarElement {
|
||||
margin: 10px 5px 5px;
|
||||
padding: 5px;
|
||||
background-color: #a8b2de;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.SettingSidebarElement:hover {
|
||||
font-weight: bolder;
|
||||
background-color: #7d8dd4;
|
||||
box-shadow: #7d8dd4 0 0 0 5px;
|
||||
transition: all 300ms;
|
||||
cursor: pointer;
|
||||
}
|
@ -16,26 +16,33 @@ describe('<RandomPage/>', function () {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('received text renders into dom', function () {
|
||||
it('simulate topic clicka', function () {
|
||||
const wrapper = shallow(<SettingsPage/>);
|
||||
|
||||
wrapper.setState({
|
||||
text: [
|
||||
"firstline",
|
||||
"secline"
|
||||
]
|
||||
});
|
||||
simulateSideBarClick("General", wrapper);
|
||||
expect(wrapper.state().currentpage).toBe("general");
|
||||
expect(wrapper.find(".SettingsContent").find("GeneralSettings")).toHaveLength(1);
|
||||
|
||||
expect(wrapper.find(".indextextarea").find(".textarea-element")).toHaveLength(2);
|
||||
simulateSideBarClick("Movies", wrapper);
|
||||
expect(wrapper.state().currentpage).toBe("movies");
|
||||
expect(wrapper.find(".SettingsContent").find("MovieSettings")).toHaveLength(1);
|
||||
|
||||
simulateSideBarClick("TV Shows", wrapper);
|
||||
expect(wrapper.state().currentpage).toBe("tv");
|
||||
expect(wrapper.find(".SettingsContent").find("span")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('test simulate reindex', function () {
|
||||
global.fetch = prepareFetchApi({});
|
||||
function simulateSideBarClick(name, wrapper) {
|
||||
wrapper.find(".SettingSidebarElement").findWhere(it =>
|
||||
it.text() === name &&
|
||||
it.type() === "div").simulate("click");
|
||||
}
|
||||
|
||||
it('simulate unknown topic', function () {
|
||||
const wrapper = shallow(<SettingsPage/>);
|
||||
wrapper.setState({currentpage: "unknown"});
|
||||
|
||||
wrapper.find(".reindexbtn").simulate("click");
|
||||
expect(wrapper.find(".SettingsContent").text()).toBe("unknown button clicked");
|
||||
|
||||
// initial send of reindex request to server
|
||||
expect(global.fetch).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user