fix some tests

fix merge issues
This commit is contained in:
2020-12-29 19:39:30 +00:00
parent e11f021efe
commit 80a04456e6
74 changed files with 8067 additions and 4481 deletions

View File

@ -1,43 +0,0 @@
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 '../../utils/GlobalInfos';
import ActorPage from '../../pages/ActorPage/ActorPage';
class ActorTile extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div className={style.actortile} onClick={() => this.handleActorClick(this.props.actor)}>
<div className={style.actortile_thumbnail}>
{this.props.actor.thumbnail === null ? <FontAwesomeIcon style={{
lineHeight: '130px'
}} icon={faUser} size='5x'/> : 'dfdf' /* todo render picture provided here! */}
</div>
<div className={style.actortile_name}>{this.props.actor.name}</div>
</div>
);
}
/**
* event handling for actor tile click
*/
handleActorClick(actor) {
// if clicklistender is defined use this one
if (this.props.onClick) {
this.props.onClick(actor.id);
return;
}
// Redirect to actor page
GlobalInfos.getViewBinding().changeRootElement(<ActorPage actor={actor}/>);
}
}
export default ActorTile;

View File

@ -5,12 +5,12 @@
float: left;
height: 200px;
margin: 3px;
width: 130px;
transition: opacity ease 0.5s;
width: 130px;
}
.actortile:hover{
.actortile:hover {
opacity: 0.7;
transition: opacity ease 0.5s;
}

View File

@ -4,24 +4,13 @@ import ActorTile from './ActorTile';
describe('<ActorTile/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}}/>);
const wrapper = shallow(<ActorTile actor={{thumbnail: '-1', name: 'testname', id: 3}}/>);
wrapper.unmount();
});
it('simulate click', function () {
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}}/>);
const func = jest.fn();
prepareViewBinding(func);
wrapper.simulate('click');
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 wrapper = shallow(<ActorTile actor={{thumbnail: '-1', name: 'testname', id: 3}} onClick={() => func()}/>);
const func1 = jest.fn();
prepareViewBinding(func1);

View File

@ -0,0 +1,49 @@
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 {Link} from 'react-router-dom';
import {ActorType} from '../../api/VideoTypes';
interface props {
actor: ActorType;
onClick?: (actor: ActorType) => void
}
class ActorTile extends React.Component<props> {
constructor(props: props) {
super(props);
this.state = {};
}
render(): JSX.Element {
if (this.props.onClick) {
return this.renderActorTile(this.props.onClick);
} else {
return (
<Link to={{pathname: '/actors/' + this.props.actor.actor_id}}>
{this.renderActorTile(() => {
})}
</Link>
);
}
}
renderActorTile(customclickhandler: (actor: ActorType) => void): JSX.Element {
console.log(this.props.actor);
return (
<div className={style.actortile} onClick={(): void => customclickhandler(this.props.actor)}>
<div className={style.actortile_thumbnail}>
{this.props.actor.thumbnail === null ? <FontAwesomeIcon style={{
lineHeight: '130px'
}} icon={faUser} size='5x'/> : 'dfdf' /* todo render picture provided here! */}
</div>
<div className={style.actortile_name}>{this.props.actor.name}</div>
</div>
);
}
}
export default ActorTile;

View File

@ -0,0 +1,8 @@
.button {
background-color: green;
border-radius: 5px;
border-width: 0;
color: white;
margin-right: 15px;
padding: 6px;
}

View File

@ -0,0 +1,32 @@
import {shallow} from 'enzyme';
import React from 'react';
import {Button} from './Button';
function prepareFetchApi(response) {
const mockJsonPromise = Promise.resolve(response);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise
});
return (jest.fn().mockImplementation(() => mockFetchPromise));
}
describe('<Button/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<Button onClick={() => {}} title='test'/>);
wrapper.unmount();
});
it('renders title', function () {
const wrapper = shallow(<Button onClick={() => {}} title='test1'/>);
expect(wrapper.text()).toBe('test1');
});
it('test onclick handling', () => {
const func = jest.fn();
const wrapper = shallow(<Button onClick={func} title='test1'/>);
wrapper.find('button').simulate('click');
expect(func).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,16 @@
import React from 'react';
import style from './Button.module.css';
interface ButtonProps {
title: string;
onClick?: () => void;
color?: React.CSSProperties;
}
export function Button(props: ButtonProps): JSX.Element {
return (
<button className={style.button} style={props.color} onClick={props.onClick}>
{props.title}
</button>
);
}

View File

@ -2,11 +2,16 @@ import React from 'react';
import style from './PageTitle.module.css';
import GlobalInfos from '../../utils/GlobalInfos';
interface props {
title: string;
subtitle: string | null;
}
/**
* Component for generating PageTitle with bottom Line
*/
class PageTitle extends React.Component {
render() {
class PageTitle extends React.Component<props> {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div className={style.pageheader + ' ' + themeStyle.backgroundcolor}>
@ -26,7 +31,7 @@ class PageTitle extends React.Component {
* use this for horizontal lines to use the current active theming
*/
export class Line extends React.Component {
render() {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<>

View File

@ -1,11 +1,11 @@
.newactorbutton {
background-color: green;
border-radius: 5px;
border-width: 0;
color: white;
margin-right: 15px;
margin-top: 12px;
padding: 6px;
background-color: green;
width: 140px;
width: 140px;
}

View File

@ -40,12 +40,12 @@ describe('<AddActorPopup/>', function () {
it('simulate actortile click', function () {
const func = jest.fn();
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
const wrapper = shallow(<AddActorPopup onHide={() => {func();}} movie_id={1}/>);
global.callAPIMock({result: 'success'});
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').props().onClick();
wrapper.setState({actors: [{actor_id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').dive().simulate('click');
expect(callAPI).toHaveBeenCalledTimes(1);
@ -55,12 +55,12 @@ describe('<AddActorPopup/>', function () {
it('test failing actortile click', function () {
const func = jest.fn();
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
const wrapper = shallow(<AddActorPopup onHide={() => {func();}}/>);
global.callAPIMock({result: 'nosuccess'});
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').props().onClick();
wrapper.setState({actors: [{actor_id: 1, actorname: 'test'}]}, () => {
wrapper.find('ActorTile').dive().simulate('click');
expect(callAPI).toHaveBeenCalledTimes(1);
@ -68,4 +68,10 @@ describe('<AddActorPopup/>', function () {
expect(func).toHaveBeenCalledTimes(0);
});
});
it('test no actor on loading', function () {
const wrapper = shallow(<AddActorPopup/>);
expect(wrapper.find('PopupBase').find('ActorTile')).toHaveLength(0);
});
});

View File

@ -4,37 +4,51 @@ import ActorTile from '../../ActorTile/ActorTile';
import style from './AddActorPopup.module.css';
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
import {callAPI} from '../../../utils/Api';
import {ActorType} from '../../../api/VideoTypes';
import {GeneralSuccess} from '../../../api/GeneralTypes';
interface props {
onHide: () => void;
movie_id: number;
}
interface state {
contentDefault: boolean;
actors: ActorType[];
}
/**
* Popup for Adding a new Actor to a Video
*/
class AddActorPopup extends React.Component {
constructor(props) {
class AddActorPopup extends React.Component<props, state> {
constructor(props: props) {
super(props);
this.state = {
contentDefault: true,
actors: undefined
actors: []
};
this.tileClickHandler = this.tileClickHandler.bind(this);
}
render() {
render(): JSX.Element {
return (
<>
{/* todo render actor tiles here and add search field*/}
<PopupBase title='Add new Actor to Video' onHide={this.props.onHide} banner={
<button
className={style.newactorbutton}
onClick={() => {this.setState({contentDefault: false});}}>Create new Actor</button>}>
onClick={(): void => {
this.setState({contentDefault: false});
}}>Create new Actor</button>}>
{this.resolvePage()}
</PopupBase>
</>
);
}
componentDidMount() {
componentDidMount(): void {
// fetch the available actors
this.loadActors();
}
@ -43,9 +57,9 @@ class AddActorPopup extends React.Component {
* selector for current showing popup page
* @returns {JSX.Element}
*/
resolvePage() {
resolvePage(): JSX.Element {
if (this.state.contentDefault) return (this.getContent());
else return (<NewActorPopupContent onHide={() => {
else return (<NewActorPopupContent onHide={(): void => {
this.loadActors();
this.setState({contentDefault: true});
}}/>);
@ -55,8 +69,8 @@ class AddActorPopup extends React.Component {
* returns content for the newActor popup
* @returns {JSX.Element}
*/
getContent() {
if (this.state.actors) {
getContent(): JSX.Element {
if (this.state.actors.length !== 0) {
return (<div>
{this.state.actors.map((el) => (<ActorTile actor={el} onClick={this.tileClickHandler}/>))}
</div>);
@ -68,9 +82,13 @@ class AddActorPopup extends React.Component {
/**
* event handling for ActorTile Click
*/
tileClickHandler(actorid) {
tileClickHandler(actor: ActorType): void {
// fetch the available actors
callAPI('actor.php', {action: 'addActorToVideo', actorid: actorid, videoid: this.props.movie_id}, result => {
callAPI<GeneralSuccess>('actor.php', {
action: 'addActorToVideo',
actorid: actor.actor_id,
videoid: this.props.movie_id
}, result => {
if (result.result === 'success') {
// return back to player page
this.props.onHide();
@ -81,8 +99,8 @@ class AddActorPopup extends React.Component {
});
}
loadActors() {
callAPI('actor.php', {action: 'getAllActors'}, result => {
loadActors(): void {
callAPI<ActorType[]>('actor.php', {action: 'getAllActors'}, result => {
this.setState({actors: result});
});
}

View File

@ -15,6 +15,7 @@ class AddTagPopup extends React.Component {
componentDidMount() {
callAPI('tags.php', {action: 'getAllTags'}, (result) => {
console.log(result);
this.setState({
items: result
});
@ -26,9 +27,10 @@ class AddTagPopup extends React.Component {
<PopupBase title='Add a Tag to this Video:' onHide={this.props.onHide}>
{this.state.items ?
this.state.items.map((i) => (
<Tag onclick={() => {
this.addTag(i.tag_id, i.tag_name);
}}>{i.tag_name}</Tag>
<Tag tagInfo={i}
onclick={() => {
this.addTag(i.tag_id, i.tag_name);
}}/>
)) : null}
</PopupBase>
);

View File

@ -34,7 +34,7 @@ describe('<AddTagPopup/>', function () {
});
it('test addtag', done => {
const wrapper = shallow(<AddTagPopup/>);
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
global.fetch = prepareFetchApi({result: 'success'});
@ -57,7 +57,7 @@ describe('<AddTagPopup/>', function () {
});
it('test failing addTag', done => {
const wrapper = shallow(<AddTagPopup/>);
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
global.fetch = prepareFetchApi({result: 'fail'});

View File

@ -22,7 +22,7 @@ describe('<NewActorPopupContent/>', () => {
global.callAPIMock({});
const func = jest.fn();
const wrapper = shallow(<NewActorPopupContent onHide={() => {func()}}/>);
const wrapper = shallow(<NewActorPopupContent onHide={() => {func();}}/>);
// manually set typed in actorname
wrapper.instance().value = 'testactorname';

View File

@ -2,12 +2,17 @@ import React from 'react';
import PopupBase from '../PopupBase';
import style from './NewActorPopup.module.css';
import {callAPI} from '../../../utils/Api';
import {GeneralSuccess} from '../../../api/GeneralTypes';
interface NewActorPopupProps {
onHide: () => void;
}
/**
* creates modal overlay to define a new Tag
*/
class NewActorPopup extends React.Component {
render() {
class NewActorPopup extends React.Component<NewActorPopupProps> {
render(): JSX.Element {
return (
<PopupBase title='Add new Tag' onHide={this.props.onHide} height='200px' width='400px'>
<NewActorPopupContent onHide={this.props.onHide}/>
@ -16,21 +21,17 @@ class NewActorPopup extends React.Component {
}
}
export class NewActorPopupContent extends React.Component {
constructor(props, context) {
super(props, context);
export class NewActorPopupContent extends React.Component<NewActorPopupProps> {
value: string | undefined;
this.props = props;
}
render() {
render(): JSX.Element {
return (
<>
<div>
<input type='text' placeholder='Actor Name' onChange={(v) => {
<input type='text' placeholder='Actor Name' onChange={(v): void => {
this.value = v.target.value;
}}/></div>
<button className={style.savebtn} onClick={() => this.storeselection()}>Save</button>
<button className={style.savebtn} onClick={(): void => this.storeselection()}>Save</button>
</>
);
}
@ -38,11 +39,11 @@ export class NewActorPopupContent extends React.Component {
/**
* store the filled in form to the backend
*/
storeselection() {
storeselection(): void {
// check if user typed in name
if (this.value === '' || this.value === undefined) return;
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result) => {
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result: GeneralSuccess) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);

View File

@ -3,8 +3,6 @@ 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 () {
@ -24,6 +22,8 @@ describe('<NewTagPopup/>', function () {
}
});
wrapper.instance().value = 'testvalue';
wrapper.find('button').simulate('click');
expect(global.fetch).toHaveBeenCalledTimes(1);

View File

@ -1,7 +1,7 @@
import React from "react";
import PopupBase from "../PopupBase";
import style from "../NewActorPopup/NewActorPopup.module.css";
import {setCustomBackendDomain} from "../../../utils/Api";
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
@ -11,10 +11,10 @@ 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) => {
<input type='text' placeholder='http://192.168.0.2' onChange={(v): void => {
setCustomBackendDomain(v.target.value);
}}/></div>
<button className={style.savebtn} onClick={() => props.onHide()}>Refresh</button>
<button className={style.savebtn} onClick={(): void => props.onHide()}>Refresh</button>
</PopupBase>
);
}

View File

@ -85,7 +85,7 @@ class PopupBase extends React.Component {
let xOld = 0, yOld = 0;
const elmnt = this.wrapperRef.current;
if(elmnt === null) return;
if (elmnt === null) return;
elmnt.firstChild.onmousedown = dragMouseDown;

View File

@ -1,9 +1,9 @@
.popup {
border: 3px #3574fe solid;
border-radius: 18px;
min-height: 80%;
height: fit-content;
left: 20%;
min-height: 80%;
opacity: 0.95;
position: absolute;
top: 10%;
@ -11,7 +11,7 @@
z-index: 2;
}
.header{
.header {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
@ -20,20 +20,20 @@
.title {
cursor: move;
float: left;
font-size: x-large;
margin-left: 15px;
margin-top: 10px;
opacity: 1;
float: left;
width: 60%;
}
.banner {
width: 40%;
float: left;
display: flex;
flex-direction: row;
float: left;
justify-content: flex-end;
width: 40%;
}
.content {

View File

@ -1,6 +1,6 @@
import {shallow} from "enzyme";
import React from "react";
import PopupBase from "./PopupBase";
import {shallow} from 'enzyme';
import React from 'react';
import PopupBase from './PopupBase';
describe('<PopupBase/>', function () {
it('renders without crashing ', function () {

View File

@ -1,7 +1,7 @@
import React from 'react';
import style from './Preview.module.css';
import Player from '../../pages/Player/Player';
import {Spinner} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import GlobalInfos from '../../utils/GlobalInfos';
import {callAPIPlain} from '../../utils/Api';
@ -31,33 +31,25 @@ class Preview extends React.Component {
render() {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}
onClick={() => this.itemClick()}>
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.state.name}</div>
<div className={style.previewpic}>
{this.state.previewpicture !== null ?
<img className={style.previewimage}
src={this.state.previewpicture}
alt='Pic loading.'/> :
<span className={style.loadAnimation}><Spinner animation='border'/></span>}
<Link to={'/player/' + this.props.movie_id}>
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.state.name}</div>
<div className={style.previewpic}>
{this.state.previewpicture !== null ?
<img className={style.previewimage}
src={this.state.previewpicture}
alt='Pic loading.'/> :
<span className={style.loadAnimation}><Spinner animation='border'/></span>}
</div>
<div className={style.previewbottom}>
</div>
<div className={style.previewbottom}>
</div>
</div>
</div>
</Link>
);
}
/**
* handle the click event of a tile
*/
itemClick() {
console.log('item clicked!' + this.state.name);
GlobalInfos.getViewBinding().changeRootElement(
<Player movie_id={this.props.movie_id}/>);
}
}
/**
@ -68,21 +60,13 @@ export class TagPreview extends React.Component {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div
className={style.videopreview + ' ' + style.tagpreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}
onClick={() => this.itemClick()}>
className={style.videopreview + ' ' + style.tagpreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
<div className={style.tagpreviewtitle + ' ' + themeStyle.lighttextcolor}>
{this.props.name}
</div>
</div>
);
}
/**
* handle the click event of a Tag tile
*/
itemClick() {
this.props.categorybinding(this.props.name);
}
}
export default Preview;

View File

@ -5,23 +5,10 @@ import Preview, {TagPreview} from './Preview';
describe('<Preview/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<Preview/>);
const wrapper = shallow(<Preview movie_id={1}/>);
wrapper.unmount();
});
it('click event triggered', () => {
const func = jest.fn();
const wrapper = shallow(<Preview/>);
prepareViewBinding(func);
wrapper.find('.videopreview').simulate('click');
//callback to open player should have called
expect(func).toHaveBeenCalledTimes(1);
});
it('picture rendered correctly', done => {
const mockSuccessResponse = 'testsrc';
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
@ -30,7 +17,7 @@ describe('<Preview/>', function () {
});
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const wrapper = shallow(<Preview name='test'/>);
const wrapper = shallow(<Preview name='test' movie_id={1}/>);
// now called 1 times
expect(global.fetch).toHaveBeenCalledTimes(1);
@ -48,7 +35,7 @@ describe('<Preview/>', function () {
});
it('spinner loads correctly', function () {
const wrapper = shallow(<Preview/>);
const wrapper = shallow(<Preview movie_id={1}/>);
// expect load animation to be visible
expect(wrapper.find('.loadAnimation')).toHaveLength(1);
@ -66,24 +53,5 @@ describe('<TagPreview/>', function () {
const wrapper = shallow(<TagPreview name='test'/>);
expect(wrapper.find('.tagpreviewtitle').text()).toBe('test');
});
it('click event triggered', function () {
const func = jest.fn();
const wrapper = shallow(<TagPreview/>);
wrapper.setProps({
categorybinding: () => {
func();
}
});
// first call of fetch is getting of available tags
expect(func).toHaveBeenCalledTimes(0);
wrapper.find('.videopreview').simulate('click');
// now called 1 times
expect(func).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,6 +1,9 @@
.sideinfo {
border: 2px #3574fe solid;
border-radius: 20px;
}
.sideinfogeometry {
float: left;
margin-left: 15px;
margin-top: 25px;

View File

@ -2,13 +2,20 @@ import React from 'react';
import style from './SideBar.module.css';
import GlobalInfos from '../../utils/GlobalInfos';
interface SideBarProps {
hiddenFrame?: boolean;
width?: string;
}
/**
* component for sidebar-info
*/
class SideBar extends React.Component {
render() {
class SideBar extends React.Component<SideBarProps> {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (<div className={style.sideinfo + ' ' + themeStyle.secbackground}>
const classnn = style.sideinfogeometry + ' ' + (this.props.hiddenFrame === undefined ? style.sideinfo + ' ' + themeStyle.secbackground : '');
return (<div className={classnn} style={{width: this.props.width}}>
{this.props.children}
</div>);
}
@ -18,7 +25,7 @@ class SideBar extends React.Component {
* The title of the sidebar
*/
export class SideBarTitle extends React.Component {
render() {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div className={style.sidebartitle + ' ' + themeStyle.subtextcolor}>{this.props.children}</div>
@ -30,7 +37,7 @@ export class SideBarTitle extends React.Component {
* An item of the sidebar
*/
export class SideBarItem extends React.Component {
render() {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div

View File

@ -1,35 +0,0 @@
import React from 'react';
import styles from './Tag.module.css';
import CategoryPage from '../../pages/CategoryPage/CategoryPage';
import GlobalInfos from '../../utils/GlobalInfos';
/**
* A Component representing a single Category tag
*/
class Tag extends React.Component {
render() {
return (
<button className={styles.tagbtn} onClick={() => this.TagClick()}
data-testid='Test-Tag'>{this.props.children}</button>
);
}
/**
* click handling for a Tag
*/
TagClick() {
const tag = this.props.children.toString().toLowerCase();
if (this.props.onclick) {
this.props.onclick(tag);
return;
}
// call callback functin to switch to category page with specified tag
GlobalInfos.getViewBinding().changeRootElement(
<CategoryPage category={tag}/>);
}
}
export default Tag;

View File

@ -6,33 +6,20 @@ import {shallow} from 'enzyme';
describe('<Tag/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<Tag>test</Tag>);
const wrapper = shallow(<Tag tagInfo={{tag_name: 'testname', tag_id: 1}}/>);
wrapper.unmount();
});
it('renders childs correctly', function () {
const wrapper = shallow(<Tag>test</Tag>);
const wrapper = shallow(<Tag tagInfo={{tag_name: 'test', tag_id: 1}}/>);
expect(wrapper.children().text()).toBe('test');
});
it('click event triggered and setvideo callback called', function () {
global.fetch = prepareFetchApi({});
const func = jest.fn();
prepareViewBinding(func);
const wrapper = shallow(<Tag>test</Tag>);
expect(func).toBeCalledTimes(0);
wrapper.simulate('click');
expect(func).toBeCalledTimes(1);
});
it('test custom onclick function', function () {
const func = jest.fn();
const wrapper = shallow(<Tag
tagInfo={{tag_name: 'test', tag_id: 1}}
onclick={() => {func();}}>test</Tag>);
expect(func).toBeCalledTimes(0);

47
src/elements/Tag/Tag.tsx Normal file
View File

@ -0,0 +1,47 @@
import React from 'react';
import styles from './Tag.module.css';
import {Link} from 'react-router-dom';
import {TagType} from '../../api/VideoTypes';
interface props {
onclick?: (_: string) => void
tagInfo: TagType
}
/**
* A Component representing a single Category tag
*/
class Tag extends React.Component<props> {
render(): JSX.Element {
if (this.props.onclick) {
return this.renderButton();
} else {
return (
<Link to={'/categories/' + this.props.tagInfo.tag_id}>
{this.renderButton()}
</Link>
);
}
}
renderButton(): JSX.Element {
return (
<button className={styles.tagbtn} onClick={(): void => this.TagClick()}
data-testid='Test-Tag'>{this.props.tagInfo.tag_name}</button>
);
}
/**
* click handling for a Tag
*/
TagClick(): void {
if (this.props.onclick) {
// call custom onclick handling
this.props.onclick(this.props.tagInfo.tag_name); // todo check if param is neccessary
return;
}
}
}
export default Tag;

View File

@ -1,33 +1,41 @@
import React from 'react';
import Preview from '../Preview/Preview';
import style from './VideoContainer.module.css';
import {VideoUnloadedType} from '../../api/VideoTypes';
interface props {
data: VideoUnloadedType[]
}
interface state {
loadeditems: VideoUnloadedType[];
selectionnr: number;
}
/**
* A videocontainer storing lots of Preview elements
* includes scroll handling and loading of preview infos
*/
class VideoContainer extends React.Component {
class VideoContainer extends React.Component<props, state> {
// stores current index of loaded elements
loadindex = 0;
loadindex: number = 0;
constructor(props, context) {
super(props, context);
this.data = props.data;
constructor(props: props) {
super(props);
this.state = {
loadeditems: [],
selectionnr: null
selectionnr: 0
};
}
componentDidMount() {
componentDidMount(): void {
document.addEventListener('scroll', this.trackScrolling);
this.loadPreviewBlock(16);
}
render() {
render(): JSX.Element {
return (
<div className={style.maincontent}>
{this.state.loadeditems.map(elem => (
@ -44,7 +52,7 @@ class VideoContainer extends React.Component {
);
}
componentWillUnmount() {
componentWillUnmount(): void {
this.setState({});
// unbind scroll listener when unmounting component
document.removeEventListener('scroll', this.trackScrolling);
@ -54,13 +62,13 @@ class VideoContainer extends React.Component {
* load previews to the container
* @param nr number of previews to load
*/
loadPreviewBlock(nr) {
loadPreviewBlock(nr: number): void {
console.log('loadpreviewblock called ...');
let ret = [];
for (let i = 0; i < nr; i++) {
// only add if not end
if (this.data.length > this.loadindex + i) {
ret.push(this.data[this.loadindex + i]);
if (this.props.data.length > this.loadindex + i) {
ret.push(this.props.data[this.loadindex + i]);
}
}
@ -78,7 +86,7 @@ class VideoContainer extends React.Component {
/**
* scroll event handler -> load new previews if on bottom
*/
trackScrolling = () => {
trackScrolling = (): void => {
// comparison if current scroll position is on bottom --> 200 is bottom offset to trigger load
if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) {
this.loadPreviewBlock(8);