Merge branch 'SettingsPage' into 'master'
Settings page See merge request lukas/openmediacenter!6
This commit is contained in:
		
							
								
								
									
										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);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user