Merge branch 'tat_remove' into 'master'
make tags deleteable See merge request lukas/openmediacenter!30
This commit is contained in:
		@@ -9,6 +9,7 @@ class Tags extends RequestBase {
 | 
				
			|||||||
    function initHandlers() {
 | 
					    function initHandlers() {
 | 
				
			||||||
        $this->addToDB();
 | 
					        $this->addToDB();
 | 
				
			||||||
        $this->getFromDB();
 | 
					        $this->getFromDB();
 | 
				
			||||||
 | 
					        $this->delete();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function addToDB() {
 | 
					    private function addToDB() {
 | 
				
			||||||
@@ -65,4 +66,36 @@ class Tags extends RequestBase {
 | 
				
			|||||||
            $this->commitMessage(json_encode($rows));
 | 
					            $this->commitMessage(json_encode($rows));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function delete() {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * delete a Tag with specified id
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        $this->addActionHandler("deleteTag", function () {
 | 
				
			||||||
 | 
					            $tag_id = $_POST['tagId'];
 | 
				
			||||||
 | 
					            $force = $_POST['force'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // delete key constraints first
 | 
				
			||||||
 | 
					            if ($force === "true") {
 | 
				
			||||||
 | 
					                $query = "DELETE FROM video_tags WHERE tag_id=$tag_id";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($this->conn->query($query) !== TRUE) {
 | 
				
			||||||
 | 
					                    $this->commitMessage('{"result":"' . $this->conn->error . '"}');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $query = "DELETE FROM tags WHERE tag_id=$tag_id";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($this->conn->query($query) === TRUE) {
 | 
				
			||||||
 | 
					                $this->commitMessage('{"result":"success"}');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // check if error is a constraint error
 | 
				
			||||||
 | 
					                if (preg_match('/^.*a foreign key constraint fails.*$/i', $this->conn->error)) {
 | 
				
			||||||
 | 
					                    $this->commitMessage('{"result":"not empty tag"}');
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $this->commitMessage('{"result":"' . $this->conn->eror . '"}');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import GlobalInfos from '../../utils/GlobalInfos';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
interface props {
 | 
					interface props {
 | 
				
			||||||
    title: string;
 | 
					    title: string;
 | 
				
			||||||
    subtitle: string | null;
 | 
					    subtitle: string | number | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,60 +22,14 @@ describe('<AddTagPopup/>', function () {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('test tag click', function () {
 | 
					    it('test tag click', function () {
 | 
				
			||||||
        const wrapper = shallow(<AddTagPopup/>);
 | 
					        const wrapper = shallow(<AddTagPopup submit={jest.fn()} onHide={jest.fn()}/>);
 | 
				
			||||||
        wrapper.instance().addTag = jest.fn();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        wrapper.setState({
 | 
					        wrapper.setState({
 | 
				
			||||||
            items: [{tag_id: 1, tag_name: 'test'}]
 | 
					            items: [{tag_id: 1, tag_name: 'test'}]
 | 
				
			||||||
        }, () => {
 | 
					        }, () => {
 | 
				
			||||||
            wrapper.find('Tag').first().dive().simulate('click');
 | 
					            wrapper.find('Tag').first().dive().simulate('click');
 | 
				
			||||||
            expect(wrapper.instance().addTag).toHaveBeenCalledTimes(1);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test addtag', done => {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<AddTagPopup movie_id={1}/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        global.fetch = prepareFetchApi({result: 'success'});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        wrapper.setProps({
 | 
					 | 
				
			||||||
            submit: jest.fn(() => {}),
 | 
					 | 
				
			||||||
            onHide: jest.fn()
 | 
					 | 
				
			||||||
        }, () => {
 | 
					 | 
				
			||||||
            wrapper.instance().addTag(1, 'test');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            expect(global.fetch).toHaveBeenCalledTimes(1);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        process.nextTick(() => {
 | 
					 | 
				
			||||||
            expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(1);
 | 
					            expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(1);
 | 
				
			||||||
            expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
 | 
					            expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            global.fetch.mockClear();
 | 
					 | 
				
			||||||
            done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test failing addTag', done => {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<AddTagPopup movie_id={1}/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        global.fetch = prepareFetchApi({result: 'fail'});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        wrapper.setProps({
 | 
					 | 
				
			||||||
            submit: jest.fn(() => {}),
 | 
					 | 
				
			||||||
            onHide: jest.fn()
 | 
					 | 
				
			||||||
        }, () => {
 | 
					 | 
				
			||||||
            wrapper.instance().addTag(1, 'test');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            expect(global.fetch).toHaveBeenCalledTimes(1);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        process.nextTick(() => {
 | 
					 | 
				
			||||||
            expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(0);
 | 
					 | 
				
			||||||
            expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            global.fetch.mockClear();
 | 
					 | 
				
			||||||
            done();
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,6 @@ import Tag from '../../Tag/Tag';
 | 
				
			|||||||
import PopupBase from '../PopupBase';
 | 
					import PopupBase from '../PopupBase';
 | 
				
			||||||
import {callAPI} from '../../../utils/Api';
 | 
					import {callAPI} from '../../../utils/Api';
 | 
				
			||||||
import {TagType} from '../../../types/VideoTypes';
 | 
					import {TagType} from '../../../types/VideoTypes';
 | 
				
			||||||
import {GeneralSuccess} from '../../../types/GeneralTypes';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface props {
 | 
					interface props {
 | 
				
			||||||
    onHide: () => void;
 | 
					    onHide: () => void;
 | 
				
			||||||
@@ -41,29 +40,13 @@ class AddTagPopup extends React.Component<props, state> {
 | 
				
			|||||||
                    this.state.items.map((i) => (
 | 
					                    this.state.items.map((i) => (
 | 
				
			||||||
                        <Tag tagInfo={i}
 | 
					                        <Tag tagInfo={i}
 | 
				
			||||||
                             onclick={(): void => {
 | 
					                             onclick={(): void => {
 | 
				
			||||||
                                 this.addTag(i.tag_id, i.tag_name);
 | 
					                                 this.props.submit(i.tag_id, i.tag_name);
 | 
				
			||||||
 | 
					                                 this.props.onHide();
 | 
				
			||||||
                             }}/>
 | 
					                             }}/>
 | 
				
			||||||
                    )) : null}
 | 
					                    )) : null}
 | 
				
			||||||
            </PopupBase>
 | 
					            </PopupBase>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * add a new tag to this video
 | 
					 | 
				
			||||||
     * @param tagid tag id to add
 | 
					 | 
				
			||||||
     * @param tagname tag name to add
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    addTag(tagid: number, tagname: string): void {
 | 
					 | 
				
			||||||
        callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, (result: GeneralSuccess) => {
 | 
					 | 
				
			||||||
            if (result.result !== 'success') {
 | 
					 | 
				
			||||||
                console.log('error occured while writing to db -- todo error handling');
 | 
					 | 
				
			||||||
                console.log(result.result);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                this.props.submit(tagid, tagname);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            this.props.onHide();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default AddTagPopup;
 | 
					export default AddTagPopup;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/elements/Popups/SubmitPopup/SubmitPopup.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/elements/Popups/SubmitPopup/SubmitPopup.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import {shallow} from "enzyme";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import SubmitPopup from "./SubmitPopup";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<SubmitPopup/>', function () {
 | 
				
			||||||
 | 
					    it('renders without crashing ', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<SubmitPopup/>);
 | 
				
			||||||
 | 
					        wrapper.unmount();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test submit click', function () {
 | 
				
			||||||
 | 
					        const func = jest.fn();
 | 
				
			||||||
 | 
					        const wrapper = shallow(<SubmitPopup submit={() => func()}/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wrapper.find('Button').findWhere(p => p.props().title === 'Submit').simulate('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(func).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test cancel click', function () {
 | 
				
			||||||
 | 
					        const func = jest.fn();
 | 
				
			||||||
 | 
					        const wrapper = shallow(<SubmitPopup onHide={() => func()}/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wrapper.find('Button').findWhere(p => p.props().title === 'Cancel').simulate('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(func).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/elements/Popups/SubmitPopup/SubmitPopup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/elements/Popups/SubmitPopup/SubmitPopup.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PopupBase from '../PopupBase';
 | 
				
			||||||
 | 
					import {Button} from '../../GPElements/Button';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface props {
 | 
				
			||||||
 | 
					    onHide: (_: void) => void;
 | 
				
			||||||
 | 
					    submit: (_: void) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function SubmitPopup(props: props): JSX.Element {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <PopupBase title='Are you sure?' onHide={props.onHide} height='160px' width='300px'>
 | 
				
			||||||
 | 
					            <Button title='Submit' color={{backgroundColor: 'green'}} onClick={(): void => props.submit()}/>
 | 
				
			||||||
 | 
					            <Button title='Cancel' color={{backgroundColor: 'red'}} onClick={(): void => props.onHide()}/>
 | 
				
			||||||
 | 
					        </PopupBase>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,41 +7,4 @@ describe('<CategoryPage/>', function () {
 | 
				
			|||||||
        const wrapper = shallow(<CategoryPage/>);
 | 
					        const wrapper = shallow(<CategoryPage/>);
 | 
				
			||||||
        wrapper.unmount();
 | 
					        wrapper.unmount();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test new tag popup', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<CategoryPage/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
					 | 
				
			||||||
        wrapper.find('[data-testid="btnaddtag"]').simulate('click');
 | 
					 | 
				
			||||||
        // newtagpopup should be showing now
 | 
					 | 
				
			||||||
        expect(wrapper.find('NewTagPopup')).toHaveLength(1);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test add popup', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<CategoryPage/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
					 | 
				
			||||||
        wrapper.setState({popupvisible: true});
 | 
					 | 
				
			||||||
        expect(wrapper.find('NewTagPopup')).toHaveLength(1);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test hiding of popup', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<CategoryPage/>);
 | 
					 | 
				
			||||||
        wrapper.setState({popupvisible: true});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        wrapper.find('NewTagPopup').props().onHide();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('test setting of subtitle', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<CategoryPage/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(wrapper.find('PageTitle').props().subtitle).not.toBe('testtitle');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        wrapper.instance().setSubTitle('testtitle');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // test if prop of title is set correctly
 | 
					 | 
				
			||||||
        expect(wrapper.find('PageTitle').props().subtitle).toBe('testtitle');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,82 +1,25 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
					 | 
				
			||||||
import Tag from '../../elements/Tag/Tag';
 | 
					 | 
				
			||||||
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
 | 
					 | 
				
			||||||
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
					 | 
				
			||||||
import {Route, Switch} from 'react-router-dom';
 | 
					import {Route, Switch} from 'react-router-dom';
 | 
				
			||||||
import {DefaultTags} from '../../types/GeneralTypes';
 | 
					 | 
				
			||||||
import {CategoryViewWR} from './CategoryView';
 | 
					import {CategoryViewWR} from './CategoryView';
 | 
				
			||||||
import TagView from './TagView';
 | 
					import TagView from './TagView';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
interface CategoryPageState {
 | 
					 | 
				
			||||||
    popupvisible: boolean;
 | 
					 | 
				
			||||||
    subtitle: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Component for Category Page
 | 
					 * Component for Category Page
 | 
				
			||||||
 * Contains a Tag Overview and loads specific Tag videos in VideoContainer
 | 
					 * Contains a Tag Overview and loads specific Tag videos in VideoContainer
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class CategoryPage extends React.Component<{}, CategoryPageState> {
 | 
					class CategoryPage extends React.Component {
 | 
				
			||||||
    constructor(props: {}) {
 | 
					 | 
				
			||||||
        super(props);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.state = {
 | 
					 | 
				
			||||||
            popupvisible: false,
 | 
					 | 
				
			||||||
            subtitle: ''
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.setSubTitle = this.setSubTitle.bind(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    render(): JSX.Element {
 | 
					    render(): JSX.Element {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <Switch>
 | 
				
			||||||
                <PageTitle
 | 
					                <Route path='/categories/:id'>
 | 
				
			||||||
                    title='Categories'
 | 
					                    <CategoryViewWR/>
 | 
				
			||||||
                    subtitle={this.state.subtitle}/>
 | 
					                </Route>
 | 
				
			||||||
 | 
					                <Route path='/categories'>
 | 
				
			||||||
                <SideBar>
 | 
					                    <TagView/>
 | 
				
			||||||
                    <SideBarTitle>Default Tags:</SideBarTitle>
 | 
					                </Route>
 | 
				
			||||||
                    <Tag tagInfo={DefaultTags.all}/>
 | 
					            </Switch>
 | 
				
			||||||
                    <Tag tagInfo={DefaultTags.fullhd}/>
 | 
					 | 
				
			||||||
                    <Tag tagInfo={DefaultTags.hd}/>
 | 
					 | 
				
			||||||
                    <Tag tagInfo={DefaultTags.lowq}/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <Line/>
 | 
					 | 
				
			||||||
                    <button data-testid='btnaddtag' className='btn btn-success' onClick={(): void => {
 | 
					 | 
				
			||||||
                        this.setState({popupvisible: true});
 | 
					 | 
				
			||||||
                    }}>Add a new Tag!
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                </SideBar>
 | 
					 | 
				
			||||||
                <Switch>
 | 
					 | 
				
			||||||
                    <Route path='/categories/:id'>
 | 
					 | 
				
			||||||
                        <CategoryViewWR setSubTitle={this.setSubTitle}/>
 | 
					 | 
				
			||||||
                    </Route>
 | 
					 | 
				
			||||||
                    <Route path='/categories'>
 | 
					 | 
				
			||||||
                        <TagView setSubTitle={this.setSubTitle}/>
 | 
					 | 
				
			||||||
                    </Route>
 | 
					 | 
				
			||||||
                </Switch>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                {this.state.popupvisible ?
 | 
					 | 
				
			||||||
                    <NewTagPopup onHide={(): void => {
 | 
					 | 
				
			||||||
                        this.setState({popupvisible: false});
 | 
					 | 
				
			||||||
                        // this.loadTags();
 | 
					 | 
				
			||||||
                    }}/> :
 | 
					 | 
				
			||||||
                    null
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            </>
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * set the subtitle of this page
 | 
					 | 
				
			||||||
     * @param subtitle string as subtitle
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    setSubTitle(subtitle: string): void {
 | 
					 | 
				
			||||||
        this.setState({subtitle: subtitle});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default CategoryPage;
 | 
					export default CategoryPage;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import {CategoryView} from './CategoryView';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
describe('<CategoryView/>', function () {
 | 
					describe('<CategoryView/>', function () {
 | 
				
			||||||
    function instance() {
 | 
					    function instance() {
 | 
				
			||||||
        return shallow(<CategoryView match={{params: {id: 10}}}/>);
 | 
					        return shallow(<CategoryView match={{params: {id: 10}}} history={{push: jest.fn()}}/>);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('renders without crashing ', function () {
 | 
					    it('renders without crashing ', function () {
 | 
				
			||||||
@@ -21,4 +21,54 @@ describe('<CategoryView/>', function () {
 | 
				
			|||||||
        wrapper.find('button').simulate('click');
 | 
					        wrapper.find('button').simulate('click');
 | 
				
			||||||
        expect(func).toHaveBeenCalledTimes(1);
 | 
					        expect(func).toHaveBeenCalledTimes(1);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test delete of tag', function () {
 | 
				
			||||||
 | 
					        const wrapper = instance();
 | 
				
			||||||
 | 
					        callAPIMock({result: 'success'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // simulate button click
 | 
				
			||||||
 | 
					        wrapper.find('Button').props().onClick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(wrapper.instance().props.history.push).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test delete of non empty tag', function () {
 | 
				
			||||||
 | 
					        const wrapper = instance();
 | 
				
			||||||
 | 
					        callAPIMock({result: 'not empty tag'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // simulate button click
 | 
				
			||||||
 | 
					        wrapper.find('Button').props().onClick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // expect SubmitPopup showing
 | 
				
			||||||
 | 
					        expect(wrapper.find('SubmitPopup')).toHaveLength(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // mock deleteTag function
 | 
				
			||||||
 | 
					        wrapper.instance().deleteTag = jest.fn((v) => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // simulate submit
 | 
				
			||||||
 | 
					        wrapper.find('SubmitPopup').props().submit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // expect deleteTag function to have been called with force parameter
 | 
				
			||||||
 | 
					        expect(wrapper.instance().deleteTag).toHaveBeenCalledWith(true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test cancel of ', function () {
 | 
				
			||||||
 | 
					        const wrapper = instance();
 | 
				
			||||||
 | 
					        callAPIMock({result: 'not empty tag'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // simulate button click
 | 
				
			||||||
 | 
					        wrapper.find('Button').props().onClick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // expect SubmitPopup showing
 | 
				
			||||||
 | 
					        expect(wrapper.find('SubmitPopup')).toHaveLength(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // mock deleteTag function
 | 
				
			||||||
 | 
					        wrapper.instance().deleteTag = jest.fn((v) => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // simulate submit
 | 
				
			||||||
 | 
					        wrapper.find('SubmitPopup').props().onHide();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // expect deleteTag function to have been called with force parameter
 | 
				
			||||||
 | 
					        expect(wrapper.instance().deleteTag).toHaveBeenCalledTimes(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,13 +4,18 @@ import VideoContainer from '../../elements/VideoContainer/VideoContainer';
 | 
				
			|||||||
import {callAPI} from '../../utils/Api';
 | 
					import {callAPI} from '../../utils/Api';
 | 
				
			||||||
import {withRouter} from 'react-router-dom';
 | 
					import {withRouter} from 'react-router-dom';
 | 
				
			||||||
import {VideoTypes} from '../../types/ApiTypes';
 | 
					import {VideoTypes} from '../../types/ApiTypes';
 | 
				
			||||||
 | 
					import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
				
			||||||
 | 
					import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
				
			||||||
 | 
					import Tag from '../../elements/Tag/Tag';
 | 
				
			||||||
 | 
					import {DefaultTags, GeneralSuccess} from '../../types/GeneralTypes';
 | 
				
			||||||
 | 
					import {Button} from '../../elements/GPElements/Button';
 | 
				
			||||||
 | 
					import SubmitPopup from '../../elements/Popups/SubmitPopup/SubmitPopup';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CategoryViewProps extends RouteComponentProps<{ id: string }> {
 | 
					interface CategoryViewProps extends RouteComponentProps<{ id: string }> {}
 | 
				
			||||||
    setSubTitle: (title: string) => void
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CategoryViewState {
 | 
					interface CategoryViewState {
 | 
				
			||||||
    loaded: boolean
 | 
					    loaded: boolean;
 | 
				
			||||||
 | 
					    submitForceDelete: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -23,7 +28,8 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
 | 
				
			|||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = {
 | 
					        this.state = {
 | 
				
			||||||
            loaded: false
 | 
					            loaded: false,
 | 
				
			||||||
 | 
					            submitForceDelete: false
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,6 +48,20 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
 | 
				
			|||||||
    render(): JSX.Element {
 | 
					    render(): JSX.Element {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
 | 
					                <PageTitle
 | 
				
			||||||
 | 
					                    title='Categories'
 | 
				
			||||||
 | 
					                    subtitle={this.videodata.length + ' Videos'}/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <SideBar>
 | 
				
			||||||
 | 
					                    <SideBarTitle>Default Tags:</SideBarTitle>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.all}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.fullhd}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.hd}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.lowq}/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <Line/>
 | 
				
			||||||
 | 
					                    <Button title='Delete Tag' onClick={(): void => {this.deleteTag(false);}} color={{backgroundColor: 'red'}}/>
 | 
				
			||||||
 | 
					                </SideBar>
 | 
				
			||||||
                {this.state.loaded ?
 | 
					                {this.state.loaded ?
 | 
				
			||||||
                    <VideoContainer
 | 
					                    <VideoContainer
 | 
				
			||||||
                        data={this.videodata}/> : null}
 | 
					                        data={this.videodata}/> : null}
 | 
				
			||||||
@@ -51,22 +71,50 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
 | 
				
			|||||||
                            this.props.history.push('/categories');
 | 
					                            this.props.history.push('/categories');
 | 
				
			||||||
                        }}>Back to Categories
 | 
					                        }}>Back to Categories
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
 | 
					                {this.handlePopups()}
 | 
				
			||||||
            </>
 | 
					            </>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handlePopups(): JSX.Element {
 | 
				
			||||||
 | 
					        if (this.state.submitForceDelete) {
 | 
				
			||||||
 | 
					            return (<SubmitPopup
 | 
				
			||||||
 | 
					                onHide={(): void => this.setState({submitForceDelete: false})}
 | 
				
			||||||
 | 
					                submit={(): void => {this.deleteTag(true);}}/>);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return <></>;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * fetch data for a specific tag from backend
 | 
					     * fetch data for a specific tag from backend
 | 
				
			||||||
     * @param id tagid
 | 
					     * @param id tagid
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    fetchVideoData(id: number): void {
 | 
					    private fetchVideoData(id: number): void {
 | 
				
			||||||
        callAPI<VideoTypes.VideoUnloadedType[]>('video.php', {action: 'getMovies', tag: id}, result => {
 | 
					        callAPI<VideoTypes.VideoUnloadedType[]>('video.php', {action: 'getMovies', tag: id}, result => {
 | 
				
			||||||
            this.videodata = result;
 | 
					            this.videodata = result;
 | 
				
			||||||
            this.setState({loaded: true});
 | 
					            this.setState({loaded: true});
 | 
				
			||||||
            this.props.setSubTitle(this.videodata.length + ' Videos');
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * delete the current tag
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private deleteTag(force: boolean): void {
 | 
				
			||||||
 | 
					        callAPI<GeneralSuccess>('tags.php', {
 | 
				
			||||||
 | 
					            action: 'deleteTag',
 | 
				
			||||||
 | 
					            tagId: parseInt(this.props.match.params.id),
 | 
				
			||||||
 | 
					            force: force
 | 
				
			||||||
 | 
					        }, result => {
 | 
				
			||||||
 | 
					            console.log(result.result);
 | 
				
			||||||
 | 
					            if (result.result === 'success') {
 | 
				
			||||||
 | 
					                this.props.history.push('/categories');
 | 
				
			||||||
 | 
					            } else if (result.result === 'not empty tag') {
 | 
				
			||||||
 | 
					                // show submisison tag to ask if really delete
 | 
				
			||||||
 | 
					                this.setState({submitForceDelete: true});
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,4 +14,30 @@ describe('<TagView/>', function () {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        expect(wrapper.find('TagPreview')).toHaveLength(1);
 | 
					        expect(wrapper.find('TagPreview')).toHaveLength(1);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test new tag popup', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<TagView/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
				
			||||||
 | 
					        wrapper.find('[data-testid="btnaddtag"]').simulate('click');
 | 
				
			||||||
 | 
					        // newtagpopup should be showing now
 | 
				
			||||||
 | 
					        expect(wrapper.find('NewTagPopup')).toHaveLength(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test add popup', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<TagView/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
				
			||||||
 | 
					        wrapper.setState({popupvisible: true});
 | 
				
			||||||
 | 
					        expect(wrapper.find('NewTagPopup')).toHaveLength(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test hiding of popup', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<TagView/>);
 | 
				
			||||||
 | 
					        wrapper.setState({popupvisible: true});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wrapper.find('NewTagPopup').props().onHide();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(wrapper.find('NewTagPopup')).toHaveLength(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,20 +4,27 @@ import videocontainerstyle from '../../elements/VideoContainer/VideoContainer.mo
 | 
				
			|||||||
import {Link} from 'react-router-dom';
 | 
					import {Link} from 'react-router-dom';
 | 
				
			||||||
import {TagPreview} from '../../elements/Preview/Preview';
 | 
					import {TagPreview} from '../../elements/Preview/Preview';
 | 
				
			||||||
import {callAPI} from '../../utils/Api';
 | 
					import {callAPI} from '../../utils/Api';
 | 
				
			||||||
 | 
					import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
				
			||||||
 | 
					import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
				
			||||||
 | 
					import Tag from '../../elements/Tag/Tag';
 | 
				
			||||||
 | 
					import {DefaultTags} from '../../types/GeneralTypes';
 | 
				
			||||||
 | 
					import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TagViewState {
 | 
					interface TagViewState {
 | 
				
			||||||
    loadedtags: TagType[];
 | 
					    loadedtags: TagType[];
 | 
				
			||||||
 | 
					    popupvisible: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface props {
 | 
					interface props {}
 | 
				
			||||||
    setSubTitle: (title: string) => void
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagView extends React.Component<props, TagViewState> {
 | 
					class TagView extends React.Component<props, TagViewState> {
 | 
				
			||||||
    constructor(props: props) {
 | 
					    constructor(props: props) {
 | 
				
			||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = {loadedtags: []};
 | 
					        this.state = {
 | 
				
			||||||
 | 
					            loadedtags: [],
 | 
				
			||||||
 | 
					            popupvisible: false
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentDidMount(): void {
 | 
					    componentDidMount(): void {
 | 
				
			||||||
@@ -27,6 +34,23 @@ class TagView extends React.Component<props, TagViewState> {
 | 
				
			|||||||
    render(): JSX.Element {
 | 
					    render(): JSX.Element {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
 | 
					                <PageTitle
 | 
				
			||||||
 | 
					                    title='Categories'
 | 
				
			||||||
 | 
					                    subtitle={this.state.loadedtags.length + ' different Tags'}/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <SideBar>
 | 
				
			||||||
 | 
					                    <SideBarTitle>Default Tags:</SideBarTitle>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.all}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.fullhd}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.hd}/>
 | 
				
			||||||
 | 
					                    <Tag tagInfo={DefaultTags.lowq}/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <Line/>
 | 
				
			||||||
 | 
					                    <button data-testid='btnaddtag' className='btn btn-success' onClick={(): void => {
 | 
				
			||||||
 | 
					                        this.setState({popupvisible: true});
 | 
				
			||||||
 | 
					                    }}>Add a new Tag!
 | 
				
			||||||
 | 
					                    </button>
 | 
				
			||||||
 | 
					                </SideBar>
 | 
				
			||||||
                <div className={videocontainerstyle.maincontent}>
 | 
					                <div className={videocontainerstyle.maincontent}>
 | 
				
			||||||
                    {this.state.loadedtags ?
 | 
					                    {this.state.loadedtags ?
 | 
				
			||||||
                        this.state.loadedtags.map((m) => (
 | 
					                        this.state.loadedtags.map((m) => (
 | 
				
			||||||
@@ -36,6 +60,7 @@ class TagView extends React.Component<props, TagViewState> {
 | 
				
			|||||||
                        )) :
 | 
					                        )) :
 | 
				
			||||||
                        'loading'}
 | 
					                        'loading'}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                {this.handlePopups()}
 | 
				
			||||||
            </>
 | 
					            </>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -46,9 +71,21 @@ class TagView extends React.Component<props, TagViewState> {
 | 
				
			|||||||
    loadTags(): void {
 | 
					    loadTags(): void {
 | 
				
			||||||
        callAPI<TagType[]>('tags.php', {action: 'getAllTags'}, result => {
 | 
					        callAPI<TagType[]>('tags.php', {action: 'getAllTags'}, result => {
 | 
				
			||||||
            this.setState({loadedtags: result});
 | 
					            this.setState({loadedtags: result});
 | 
				
			||||||
            this.props.setSubTitle(result.length + ' different Tags');
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handlePopups(): JSX.Element {
 | 
				
			||||||
 | 
					        if (this.state.popupvisible) {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					                <NewTagPopup onHide={(): void => {
 | 
				
			||||||
 | 
					                    this.setState({popupvisible: false});
 | 
				
			||||||
 | 
					                    this.loadTags();
 | 
				
			||||||
 | 
					                }}/>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return (<></>);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default TagView;
 | 
					export default TagView;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user