build electron app

implement new fetch api calls
use typescript
This commit is contained in:
2020-12-17 20:53:22 +00:00
parent c049aa345c
commit 7d696122fa
43 changed files with 804 additions and 500 deletions

View File

@ -2,7 +2,7 @@ import style from './ActorTile.module.css';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faUser} from '@fortawesome/free-solid-svg-icons';
import React from 'react';
import GlobalInfos from '../../GlobalInfos';
import GlobalInfos from '../../utils/GlobalInfos';
import ActorPage from '../../pages/ActorPage/ActorPage';
class ActorTile extends React.Component {

View File

@ -18,4 +18,17 @@ describe('<ActorTile/>', function () {
expect(func).toBeCalledTimes(1);
});
it('simulate click with custom handler', function () {
const func = jest.fn((_) => {});
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}} onClick={() => func()}/>);
const func1 = jest.fn();
prepareViewBinding(func1);
wrapper.simulate('click');
expect(func1).toBeCalledTimes(0);
expect(func).toBeCalledTimes(1);
});
});

View File

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

View File

@ -1,7 +1,7 @@
import React from 'react';
import {shallow} from 'enzyme';
import PageTitle from './PageTitle';
import PageTitle, {Line} from './PageTitle';
describe('<Preview/>', function () {
it('renders without crashing ', function () {
@ -29,3 +29,10 @@ describe('<Preview/>', function () {
});
});
describe('<Line/>', () => {
it('renders without crashing', function () {
const wrapper = shallow(<Line/>);
wrapper.unmount();
});
});

View File

@ -3,6 +3,7 @@ import React from 'react';
import ActorTile from '../../ActorTile/ActorTile';
import style from './AddActorPopup.module.css';
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
import {callAPI} from '../../../utils/Api';
/**
* Popup for Adding a new Actor to a Video
@ -69,33 +70,21 @@ class AddActorPopup extends React.Component {
*/
tileClickHandler(actorid) {
// fetch the available actors
const req = new FormData();
req.append('action', 'addActorToVideo');
req.append('actorid', actorid);
req.append('videoid', this.props.movie_id);
fetch('/api/actor.php', {method: 'POST', body: req})
.then((response) => response.json()
.then((result) => {
if (result.result === 'success') {
// return back to player page
this.props.onHide();
} else {
console.error('an error occured while fetching actors');
console.error(result);
}
}));
callAPI('actor.php', {action: 'addActorToVideo', actorid: actorid, videoid: this.props.movie_id}, result => {
if (result.result === 'success') {
// return back to player page
this.props.onHide();
} else {
console.error('an error occured while fetching actors');
console.error(result);
}
});
}
loadActors() {
const req = new FormData();
req.append('action', 'getAllActors');
fetch('/api/actor.php', {method: 'POST', body: req})
.then((response) => response.json()
.then((result) => {
this.setState({actors: result});
}));
callAPI('actor.php', {action: 'getAllActors'}, result => {
this.setState({actors: result});
});
}
}

View File

@ -1,6 +1,7 @@
import {shallow} from 'enzyme';
import React from 'react';
import AddActorPopup from './AddActorPopup';
import {callAPI} from '../../../utils/Api';
describe('<AddActorPopup/>', function () {
it('renders without crashing ', function () {
@ -8,12 +9,63 @@ describe('<AddActorPopup/>', function () {
wrapper.unmount();
});
// it('simulate change to other page', function () {
// const wrapper = shallow(<AddActorPopup/>);
//
// console.log(wrapper.find('PopupBase').dive().debug());
//
//
// console.log(wrapper.debug());
// });
it('simulate change to other page', function () {
const wrapper = shallow(<AddActorPopup/>);
expect(wrapper.find('NewActorPopupContent')).toHaveLength(0);
wrapper.find('PopupBase').props().banner.props.onClick();
// check if new content is showing
expect(wrapper.find('NewActorPopupContent')).toHaveLength(1);
});
it('hide new actor page', function () {
const wrapper = shallow(<AddActorPopup/>);
wrapper.find('PopupBase').props().banner.props.onClick();
// call onhide event listener manually
wrapper.find('NewActorPopupContent').props().onHide();
// expect other page to be hidden again
expect(wrapper.find('NewActorPopupContent')).toHaveLength(0);
});
it('test api call and insertion of actor tiles', function () {
global.callAPIMock([{id: 1, actorname: 'test'}, {id: 2, actorname: 'test2'}]);
const wrapper = shallow(<AddActorPopup/>);
expect(wrapper.find('ActorTile')).toHaveLength(2);
});
it('simulate actortile click', function () {
const func = jest.fn();
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
global.callAPIMock({result: 'success'});
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').props().onClick();
expect(callAPI).toHaveBeenCalledTimes(1);
expect(func).toHaveBeenCalledTimes(1);
});
});
it('test failing actortile click', function () {
const func = jest.fn();
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
global.callAPIMock({result: 'nosuccess'});
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').props().onClick();
expect(callAPI).toHaveBeenCalledTimes(1);
// hide funtion should not have been called on error!
expect(func).toHaveBeenCalledTimes(0);
});
});
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import Tag from '../../Tag/Tag';
import PopupBase from '../PopupBase';
import {callAPI} from '../../../utils/Api';
/**
* component creates overlay to add a new tag to a video
@ -13,16 +14,11 @@ class AddTagPopup extends React.Component {
}
componentDidMount() {
const updateRequest = new FormData();
updateRequest.append('action', 'getAllTags');
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json())
.then((result) => {
this.setState({
items: result
});
callAPI('tags.php', {action: 'getAllTags'}, (result) => {
this.setState({
items: result
});
});
}
render() {
@ -44,23 +40,15 @@ class AddTagPopup extends React.Component {
* @param tagname tag name to add
*/
addTag(tagid, tagname) {
console.log(this.props);
const updateRequest = new FormData();
updateRequest.append('action', 'addTag');
updateRequest.append('id', tagid);
updateRequest.append('movieid', this.props.movie_id);
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json()
.then((result) => {
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();
}));
callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, result => {
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();
});
}
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import PopupBase from '../PopupBase';
import style from './NewActorPopup.module.css';
import {callAPI} from '../../../utils/Api';
/**
* creates modal overlay to define a new Tag
@ -41,19 +42,13 @@ export class NewActorPopupContent extends React.Component {
// check if user typed in name
if (this.value === '' || this.value === undefined) return;
const req = new FormData();
req.append('action', 'createActor');
req.append('actorname', this.value);
fetch('/api/actor.php', {method: 'POST', body: req})
.then((response) => response.json())
.then((result) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);
}
this.props.onHide();
});
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);
}
this.props.onHide();
});
}
}

View File

@ -3,6 +3,7 @@ import React from 'react';
import {shallow} from 'enzyme';
import '@testing-library/jest-dom';
import NewActorPopup, {NewActorPopupContent} from './NewActorPopup';
import {callAPI} from '../../../utils/Api';
describe('<NewActorPopup/>', function () {
it('renders without crashing ', function () {
@ -18,18 +19,23 @@ describe('<NewActorPopupContent/>', () => {
});
it('simulate button click', function () {
const wrapper = shallow(<NewActorPopupContent/>);
global.callAPIMock({});
const func = jest.fn();
const wrapper = shallow(<NewActorPopupContent onHide={() => {func()}}/>);
// manually set typed in actorname
wrapper.instance().value = 'testactorname';
global.fetch = prepareFetchApi({});
expect(global.fetch).toBeCalledTimes(0);
expect(callAPI).toBeCalledTimes(0);
wrapper.find('button').simulate('click');
// fetch should have been called once now
expect(global.fetch).toBeCalledTimes(1);
expect(callAPI).toBeCalledTimes(1);
expect(func).toHaveBeenCalledTimes(1);
});
it('test not allowing request if textfield is empty', function () {

View File

@ -1,6 +1,7 @@
import React from 'react';
import PopupBase from '../PopupBase';
import style from './NewTagPopup.module.css';
import {callAPI} from '../../../utils/Api';
/**
* creates modal overlay to define a new Tag
@ -27,19 +28,13 @@ class NewTagPopup extends React.Component {
* store the filled in form to the backend
*/
storeselection() {
const updateRequest = new FormData();
updateRequest.append('action', 'createTag');
updateRequest.append('tagname', this.value);
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
.then((response) => response.json())
.then((result) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);
}
this.props.onHide();
});
callAPI('tags.php', {action: 'createTag', tagname: this.value}, result => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);
}
this.props.onHide();
});
}
}

View File

@ -3,6 +3,8 @@ import React from 'react';
import {shallow} from 'enzyme';
import '@testing-library/jest-dom';
import NewTagPopup from './NewTagPopup';
import {NoBackendConnectionPopup} from '../NoBackendConnectionPopup/NoBackendConnectionPopup';
import {getBackendDomain} from '../../../utils/Api';
describe('<NewTagPopup/>', function () {
it('renders without crashing ', function () {
@ -33,4 +35,12 @@ describe('<NewTagPopup/>', function () {
done();
});
});
it('simulate textfield change', function () {
const wrapper = shallow(<NewTagPopup/>);
wrapper.find('input').simulate('change', {target: {value: 'testvalue'}});
expect(wrapper.instance().value).toBe('testvalue');
});
});

View File

@ -0,0 +1,29 @@
import {shallow} from 'enzyme';
import React from 'react';
import {NoBackendConnectionPopup} from './NoBackendConnectionPopup';
import {getBackendDomain} from '../../../utils/Api';
describe('<NoBackendConnectionPopup/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
wrapper.unmount();
});
it('hides on refresh click', function () {
const func = jest.fn();
const wrapper = shallow(<NoBackendConnectionPopup onHide={func}/>);
expect(func).toBeCalledTimes(0);
wrapper.find('button').simulate('click');
expect(func).toBeCalledTimes(1);
});
it('simulate change of textfield', function () {
const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
wrapper.find('input').simulate('change', {target: {value: 'testvalue'}});
expect(getBackendDomain()).toBe('testvalue');
});
});

View File

@ -0,0 +1,20 @@
import React from "react";
import PopupBase from "../PopupBase";
import style from "../NewActorPopup/NewActorPopup.module.css";
import {setCustomBackendDomain} from "../../../utils/Api";
interface NBCProps {
onHide: (_: void) => void
}
export function NoBackendConnectionPopup(props: NBCProps): JSX.Element {
return (
<PopupBase title='No connection to backend API!' onHide={props.onHide} height='200px' width='600px'>
<div>
<input type='text' placeholder='http://192.168.0.2' onChange={(v) => {
setCustomBackendDomain(v.target.value);
}}/></div>
<button className={style.savebtn} onClick={() => props.onHide()}>Refresh</button>
</PopupBase>
);
}

View File

@ -1,4 +1,4 @@
import GlobalInfos from '../../GlobalInfos';
import GlobalInfos from '../../utils/GlobalInfos';
import style from './PopupBase.module.css';
import {Line} from '../PageTitle/PageTitle';
import React from 'react';
@ -20,7 +20,8 @@ class PopupBase extends React.Component {
// parse style props
this.framedimensions = {
width: (this.props.width ? this.props.width : undefined),
height: (this.props.height ? this.props.height : undefined)
height: (this.props.height ? this.props.height : undefined),
minHeight: (this.props.height ? this.props.height : undefined)
};
}

View File

@ -1,7 +1,8 @@
.popup {
border: 3px #3574fe solid;
border-radius: 18px;
height: 80%;
min-height: 80%;
height: fit-content;
left: 20%;
opacity: 0.95;
position: absolute;
@ -40,4 +41,5 @@
margin-right: 20px;
margin-top: 10px;
opacity: 1;
overflow: auto;
}

View File

@ -8,5 +8,19 @@ describe('<PopupBase/>', function () {
wrapper.unmount();
});
it('simulate keypress', function () {
let events = [];
document.addEventListener = jest.fn((event, cb) => {
events[event] = cb;
});
const func = jest.fn();
shallow(<PopupBase onHide={() => func()}/>);
// trigger the keypress event
events.keyup({key: 'Escape'});
expect(func).toBeCalledTimes(1);
});
});

View File

@ -2,7 +2,8 @@ import React from 'react';
import style from './Preview.module.css';
import Player from '../../pages/Player/Player';
import {Spinner} from 'react-bootstrap';
import GlobalInfos from '../../GlobalInfos';
import GlobalInfos from '../../utils/GlobalInfos';
import {callAPIPlain} from '../../utils/Api';
/**
* Component for single preview tile
@ -19,22 +20,12 @@ class Preview extends React.Component {
}
componentDidMount() {
this.setState({
previewpicture: null,
name: this.props.name
callAPIPlain('video.php', {action: 'readThumbnail', movieid: this.props.movie_id}, (result) => {
this.setState({
previewpicture: result,
name: this.props.name
});
});
const updateRequest = new FormData();
updateRequest.append('action', 'readThumbnail');
updateRequest.append('movieid', this.props.movie_id);
fetch('/api/video.php', {method: 'POST', body: updateRequest})
.then((response) => response.text()
.then((result) => {
this.setState({
previewpicture: result
});
}));
}
render() {

View File

@ -9,12 +9,6 @@ describe('<Preview/>', function () {
wrapper.unmount();
});
// check if preview title renders correctly
it('renders title', () => {
const wrapper = shallow(<Preview name='test'/>);
expect(wrapper.find('.previewtitle').text()).toBe('test');
});
it('click event triggered', () => {
const func = jest.fn();
@ -36,7 +30,7 @@ describe('<Preview/>', function () {
});
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const wrapper = shallow(<Preview/>);
const wrapper = shallow(<Preview name='test'/>);
// now called 1 times
expect(global.fetch).toHaveBeenCalledTimes(1);
@ -44,6 +38,8 @@ describe('<Preview/>', function () {
process.nextTick(() => {
// received picture should be rendered into wrapper
expect(wrapper.find('.previewimage').props().src).not.toBeNull();
// check if preview title renders correctly
expect(wrapper.find('.previewtitle').text()).toBe('test');
global.fetch.mockClear();
done();

View File

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

View File

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