Compare commits
5 Commits
v0.1.1
...
passwordfi
Author | SHA1 | Date | |
---|---|---|---|
8c9f3aecd8 | |||
c4098a8d3d | |||
043750170b | |||
23a1fdca75 | |||
72c68d8a7c |
17
api/settings.php
Normal file
17
api/settings.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
require 'Database.php';
|
||||||
|
|
||||||
|
$conn = Database::getInstance()->getConnection();
|
||||||
|
|
||||||
|
|
||||||
|
if (isset($_POST['action'])) {
|
||||||
|
$action = $_POST['action'];
|
||||||
|
switch ($action) {
|
||||||
|
case "isPasswordNeeded":
|
||||||
|
echo '{"password": true}';
|
||||||
|
break;
|
||||||
|
case "checkPassword":
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
34
src/App.js
34
src/App.js
@ -7,11 +7,13 @@ import RandomPage from "./pages/RandomPage/RandomPage";
|
|||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import SettingsPage from "./pages/SettingsPage/SettingsPage";
|
import SettingsPage from "./pages/SettingsPage/SettingsPage";
|
||||||
import CategoryPage from "./pages/CategoryPage/CategoryPage";
|
import CategoryPage from "./pages/CategoryPage/CategoryPage";
|
||||||
|
import {Spinner} from "react-bootstrap";
|
||||||
|
import LoginPage from "./pages/LoginPage/LoginPage";
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = {page: "default"};
|
this.state = {page: "unverified"};
|
||||||
|
|
||||||
// bind this to the method for being able to call methods such as this.setstate
|
// bind this to the method for being able to call methods such as this.setstate
|
||||||
this.showVideo = this.showVideo.bind(this);
|
this.showVideo = this.showVideo.bind(this);
|
||||||
@ -42,12 +44,42 @@ class App extends React.Component {
|
|||||||
} else if (this.state.page === "lastpage") {
|
} else if (this.state.page === "lastpage") {
|
||||||
// return back to last page
|
// return back to last page
|
||||||
page = this.mypage;
|
page = this.mypage;
|
||||||
|
} else if (this.state.page === "loginpage") {
|
||||||
|
// return back to last page
|
||||||
|
page = <LoginPage/>;
|
||||||
|
} else if (this.state.page === "unverified") {
|
||||||
|
// return back to last page
|
||||||
|
page =
|
||||||
|
<div className='loadSpinner'>
|
||||||
|
<Spinner style={{marginLeft: "40px", marginBottom: "20px"}} animation="border" role="status">
|
||||||
|
<span className="sr-only">Loading...</span>
|
||||||
|
</Spinner>
|
||||||
|
<div>Content loading...</div>
|
||||||
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
page = <div>unimplemented yet!</div>;
|
page = <div>unimplemented yet!</div>;
|
||||||
}
|
}
|
||||||
return (page);
|
return (page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const updateRequest = new FormData();
|
||||||
|
updateRequest.append("action", "isPasswordNeeded");
|
||||||
|
|
||||||
|
fetch('/api/settings.php', {method: 'POST', body: updateRequest})
|
||||||
|
.then((response) => response.json()
|
||||||
|
.then((result) => {
|
||||||
|
if (result.password === false) {
|
||||||
|
this.setState({page: "default"});
|
||||||
|
} else {
|
||||||
|
this.setState({page: "loginpage"});
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.catch(() => {
|
||||||
|
console.log("no connection to backend");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
|
@ -17,4 +17,62 @@ describe('<App/>', function () {
|
|||||||
const wrapper = shallow(<App/>);
|
const wrapper = shallow(<App/>);
|
||||||
expect(wrapper.find('nav').find('li')).toHaveLength(4);
|
expect(wrapper.find('nav').find('li')).toHaveLength(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('simulate video view change ', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
|
||||||
|
wrapper.instance().showVideo(<div id='testit'></div>);
|
||||||
|
|
||||||
|
expect(wrapper.find("#testit")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test hide video again', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
|
||||||
|
wrapper.instance().showVideo(<div id='testit'></div>);
|
||||||
|
|
||||||
|
expect(wrapper.find("#testit")).toHaveLength(1);
|
||||||
|
|
||||||
|
wrapper.instance().hideVideo();
|
||||||
|
|
||||||
|
expect(wrapper.find("HomePage")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test fallback to last loaded page', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
|
||||||
|
wrapper.find(".nav-link").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click");
|
||||||
|
|
||||||
|
wrapper.instance().showVideo(<div id='testit'></div>);
|
||||||
|
|
||||||
|
expect(wrapper.find("#testit")).toHaveLength(1);
|
||||||
|
|
||||||
|
wrapper.instance().hideVideo();
|
||||||
|
|
||||||
|
expect(wrapper.find("RandomPage")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test home click', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
wrapper.setState({page: "wrongvalue"});
|
||||||
|
expect(wrapper.find("HomePage")).toHaveLength(0);
|
||||||
|
wrapper.find(".nav-link").findWhere(t => t.text() === "Home" && t.type() === "div").simulate("click");
|
||||||
|
expect(wrapper.find("HomePage")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test category click', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
|
||||||
|
expect(wrapper.find("CategoryPage")).toHaveLength(0);
|
||||||
|
wrapper.find(".nav-link").findWhere(t => t.text() === "Categories" && t.type() === "div").simulate("click");
|
||||||
|
expect(wrapper.find("CategoryPage")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test settings click', function () {
|
||||||
|
const wrapper = shallow(<App/>);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,3 +10,8 @@
|
|||||||
.nav-link:hover {
|
.nav-link:hover {
|
||||||
color: rgba(255, 255, 255, 1);
|
color: rgba(255, 255, 255, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadSpinner {
|
||||||
|
margin-top: 200px;
|
||||||
|
margin-left: 50%;
|
||||||
|
}
|
||||||
|
@ -39,7 +39,7 @@ describe('<CategoryPage/>', function () {
|
|||||||
let message;
|
let message;
|
||||||
global.console.log = jest.fn((m) => {
|
global.console.log = jest.fn((m) => {
|
||||||
message = m;
|
message = m;
|
||||||
})
|
});
|
||||||
|
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
const wrapper = shallow(<CategoryPage/>);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
width: 10%;
|
width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchform{
|
.searchform {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
@ -124,16 +124,16 @@ class HomePage extends React.Component {
|
|||||||
<div className='pageheader'>
|
<div className='pageheader'>
|
||||||
<span className='pageheadertitle'>Home Page</span>
|
<span className='pageheadertitle'>Home Page</span>
|
||||||
<span className='pageheadersubtitle'>{this.state.tag} Videos - {this.state.selectionnr}</span>
|
<span className='pageheadersubtitle'>{this.state.tag} Videos - {this.state.selectionnr}</span>
|
||||||
<form className="form-inline searchform" action="#">
|
<form className="form-inline searchform" onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.searchVideos(this.keyword);
|
||||||
|
}}>
|
||||||
<input data-testid='searchtextfield' className="form-control mr-sm-2"
|
<input data-testid='searchtextfield' className="form-control mr-sm-2"
|
||||||
type="text" placeholder="Search"
|
type="text" placeholder="Search"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
this.keyword = e.target.value
|
this.keyword = e.target.value
|
||||||
}}/>
|
}}/>
|
||||||
<button data-testid='searchbtnsubmit' className="btn btn-success" type="submit" onClick={() => {
|
<button data-testid='searchbtnsubmit' className="btn btn-success" type="submit">Search</button>
|
||||||
this.searchVideos(this.keyword)
|
|
||||||
}}>Search
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,11 @@ function prepareFetchApi(response) {
|
|||||||
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareFailingFetchApi() {
|
||||||
|
const mockFetchPromise = Promise.reject("myreason");
|
||||||
|
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||||
|
}
|
||||||
|
|
||||||
describe('<HomePage/>', function () {
|
describe('<HomePage/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
const wrapper = shallow(<HomePage/>);
|
const wrapper = shallow(<HomePage/>);
|
||||||
@ -58,11 +63,11 @@ describe('<HomePage/>', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('test search field', done => {
|
it('test search field', done => {
|
||||||
global.fetch = prepareFetchApi([{},{}]);
|
global.fetch = prepareFetchApi([{}, {}]);
|
||||||
|
|
||||||
const wrapper = shallow(<HomePage/>);
|
const wrapper = shallow(<HomePage/>);
|
||||||
|
|
||||||
wrapper.find('[data-testid="searchtextfield"]').simulate('change', { target: { value: 'testvalue' } });
|
wrapper.find('[data-testid="searchtextfield"]').simulate('change', {target: {value: 'testvalue'}});
|
||||||
wrapper.find('[data-testid="searchbtnsubmit"]').simulate("click");
|
wrapper.find('[data-testid="searchbtnsubmit"]').simulate("click");
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
@ -73,4 +78,43 @@ describe('<HomePage/>', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test form submit', done => {
|
||||||
|
global.fetch = prepareFetchApi([{}, {}]);
|
||||||
|
|
||||||
|
const wrapper = shallow(<HomePage/>);
|
||||||
|
|
||||||
|
const fakeEvent = {preventDefault: () => console.log('preventDefault')};
|
||||||
|
wrapper.find(".searchform").simulate('submit', fakeEvent);
|
||||||
|
|
||||||
|
expect(wrapper.state().selectionnr).toBe(0);
|
||||||
|
|
||||||
|
process.nextTick(() => {
|
||||||
|
// state to be set correctly with response
|
||||||
|
expect(wrapper.state().selectionnr).toBe(2);
|
||||||
|
|
||||||
|
global.fetch.mockClear();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test no backend connection behaviour', done => {
|
||||||
|
// this test assumes a console.log within every connection fail
|
||||||
|
global.fetch = prepareFailingFetchApi();
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
global.console.log = jest.fn((m) => {
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = shallow(<HomePage/>);
|
||||||
|
|
||||||
|
process.nextTick(() => {
|
||||||
|
// state to be set correctly with response
|
||||||
|
expect(global.fetch).toBeCalledTimes(2);
|
||||||
|
|
||||||
|
global.fetch.mockClear();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
60
src/pages/LoginPage/LoginPage.js
Normal file
60
src/pages/LoginPage/LoginPage.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {Form} from "react-bootstrap";
|
||||||
|
|
||||||
|
class LoginPage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
|
|
||||||
|
this.props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='pageheader'>
|
||||||
|
<span className='pageheadertitle'>Login Page</span>
|
||||||
|
<span className='pageheadersubtitle'>type correct password!</span>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div style={{marginLeft: "35%", width: "400px", marginTop: "100px"}}>
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Label>Password</Form.Label>
|
||||||
|
<Form.Control id='passfield' type="password" placeholder="Enter Password" onChange={(v) => {
|
||||||
|
this.password = v.target.value
|
||||||
|
}}/>
|
||||||
|
<Form.Text className="text-muted">
|
||||||
|
You can disable/enable this feature on settingspage.
|
||||||
|
</Form.Text>
|
||||||
|
<hr/>
|
||||||
|
<button className='btn btn-success' type='submit' onClick={() => this.checkPassword()}>Submit
|
||||||
|
</button>
|
||||||
|
</Form.Group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPassword() {
|
||||||
|
const updateRequest = new FormData();
|
||||||
|
updateRequest.append("action", "checkPassword");
|
||||||
|
updateRequest.append("password", this.state.password);
|
||||||
|
|
||||||
|
fetch('/api/settings.php', {method: 'POST', body: updateRequest})
|
||||||
|
.then((response) => response.json()
|
||||||
|
.then((result) => {
|
||||||
|
if (result.correct) {
|
||||||
|
// todo 2020-06-18: call a callback to return back to right page
|
||||||
|
} else {
|
||||||
|
// error popup
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.catch(() => {
|
||||||
|
console.log("no connection to backend");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginPage;
|
@ -34,7 +34,10 @@ class SettingsPage extends React.Component {
|
|||||||
if (this.myinterval) {
|
if (this.myinterval) {
|
||||||
clearInterval(this.myinterval);
|
clearInterval(this.myinterval);
|
||||||
}
|
}
|
||||||
|
// todo 2020-06-18: maybe not start on mount
|
||||||
this.myinterval = setInterval(this.updateStatus, 1000);
|
this.myinterval = setInterval(this.updateStatus, 1000);
|
||||||
|
|
||||||
|
// todo 2020-06-18: fetch path data here
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
Reference in New Issue
Block a user