Add API models, mock backend, more tests
This commit is contained in:
@@ -0,0 +1 @@
|
||||
API_URL=http://localhost:1234
|
||||
@@ -6,3 +6,4 @@ tests/jest/__coverage__/
|
||||
tests/jest/**/*.jsx
|
||||
tests/testcafe/screenshots
|
||||
.vscode/
|
||||
.env
|
||||
@@ -3,6 +3,7 @@ const {resolve} = require('path');
|
||||
const {CheckerPlugin} = require('awesome-typescript-loader');
|
||||
const StyleLintPlugin = require('stylelint-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const Dotenv = require('dotenv-webpack');
|
||||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
@@ -46,6 +47,12 @@ module.exports = {
|
||||
new CheckerPlugin(),
|
||||
new StyleLintPlugin(),
|
||||
new HtmlWebpackPlugin({template: 'index.html.ejs',}),
|
||||
new Dotenv({
|
||||
path: './.env.sample',
|
||||
}),
|
||||
new Dotenv({
|
||||
path: './.env',
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
'react': 'React',
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"posts": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "json-server",
|
||||
"author": "typicode"
|
||||
}
|
||||
],
|
||||
"comments": [
|
||||
{
|
||||
"id": 1,
|
||||
"body": "some comment",
|
||||
"postId": 1
|
||||
}
|
||||
],
|
||||
"profile": {
|
||||
"name": "typicode"
|
||||
}
|
||||
}
|
||||
Generated
+548
-1913
File diff suppressed because it is too large
Load Diff
+7
-2
@@ -25,8 +25,9 @@
|
||||
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
|
||||
"serve": "node express.js",
|
||||
"start-prod": "npm run build && npm run serve",
|
||||
"mock-backend": "json-server --watch db.json -p 1234",
|
||||
"test": "npm-run-all lint test:e2e",
|
||||
"test:e2e": "npm-run-all -s build -p -r serve test:e2e:run",
|
||||
"test:e2e": "npm-run-all -s build -p -r mock-backend serve test:e2e:run",
|
||||
"test:e2e:run": "testcafe -S -s 'tests/testcafe/screenshots' --app-init-delay 2000 chrome tests/testcafe",
|
||||
"plop": "plop"
|
||||
},
|
||||
@@ -51,6 +52,7 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^1.0.0-rc.9",
|
||||
"image-webpack-loader": "^4.3.0",
|
||||
"json-server": "^0.14.0",
|
||||
"node-sass": "^4.9.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"plop": "^2.0.0",
|
||||
@@ -76,8 +78,11 @@
|
||||
"webpack-merge": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"dotenv-webpack": "^1.5.7",
|
||||
"mobx": "^5.0.3",
|
||||
"mobx-react": "^5.2.3"
|
||||
"mobx-react": "^5.2.3",
|
||||
"normalize.css": "^8.0.0"
|
||||
},
|
||||
"postcss": {}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ export interface {{ properCase name }}Props {}
|
||||
{{#if observer}}
|
||||
export default (props) => <{{ properCase name }} {{ camelCase store_name }}={ {{ camelCase store_name }} } { ...props } />;
|
||||
{{else}}
|
||||
export default <{{ properCase name }} />;
|
||||
export default {{ properCase name }};
|
||||
{{/if}}
|
||||
@@ -1,28 +1,64 @@
|
||||
import * as React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import "./App.scss";
|
||||
import appState from "../../stores/AppStore";
|
||||
import appStore from "../../stores/AppStore";
|
||||
import Button from "../Button";
|
||||
import { getPosts, Post as PostInterface } from "../../models/post";
|
||||
import Post from "../Post";
|
||||
|
||||
export interface AppProps {
|
||||
appState: {
|
||||
appStore: {
|
||||
increment: () => string,
|
||||
counter: number
|
||||
};
|
||||
}
|
||||
|
||||
@observer class App extends React.Component<AppProps, undefined> {
|
||||
export interface AppState {
|
||||
posts: PostInterface[];
|
||||
error: string | Error;
|
||||
}
|
||||
|
||||
@observer class App extends React.Component<AppProps, AppState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.fetchPosts = this.fetchPosts.bind(this);
|
||||
this.state = {
|
||||
error: "",
|
||||
posts: [],
|
||||
};
|
||||
this.fetchPosts();
|
||||
}
|
||||
|
||||
async fetchPosts() {
|
||||
try {
|
||||
const posts = await getPosts();
|
||||
this.setState({
|
||||
posts,
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({
|
||||
error: "Failed to get posts! Is the mock backend on?",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="app__landing">
|
||||
<h1>Aalto-yliopiston sähköinsinöörikilta!</h1>
|
||||
<p>Sähköä, viinaa, naisia.</p>
|
||||
<Button onClick={this.props.appState.increment}>{this.props.appState.counter}</Button>
|
||||
<div className="app__landing__container--button">
|
||||
<h2>Increment button (MobX)</h2>
|
||||
<Button onClick={this.props.appStore.increment}>{this.props.appStore.counter}</Button>
|
||||
</div>
|
||||
<div className="app__landing__container--posts">
|
||||
<h2>Posts from mock server</h2>
|
||||
{ this.state.posts.map((post, index) => <Post key={index} post={post} />)}
|
||||
</div>
|
||||
<div className="app__landing__container--error">
|
||||
{ this.state.error }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default props => <App appState={appState} {...props} />;
|
||||
export default props => <App appStore={appStore} {...props} />;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
&:hover {
|
||||
border: 2px solid $dark-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:active,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.post {
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as React from "react";
|
||||
import "./Post.scss";
|
||||
import { Post as PostInterface } from "../../models/posts";
|
||||
|
||||
export interface PostProps {
|
||||
post: PostInterface;
|
||||
}
|
||||
|
||||
class Post extends React.Component<PostProps, undefined> {
|
||||
render() {
|
||||
const { title, author, id } = this.props.post;
|
||||
return (
|
||||
<div className="post">
|
||||
#{ id }: <strong>{ title }</strong> from <i>{ author }</i>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Post;
|
||||
@@ -0,0 +1,2 @@
|
||||
import Post from "./Post";
|
||||
export default Post;
|
||||
@@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import {render} from "react-dom";
|
||||
import {AppContainer} from "react-hot-loader";
|
||||
import App from "./components/App";
|
||||
import "normalize.css";
|
||||
import "./index.scss";
|
||||
|
||||
const rootEl = document.getElementById("root");
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import axios from "axios";
|
||||
|
||||
const url = `${process.env.API_URL}/posts`;
|
||||
|
||||
export interface Post {
|
||||
id: number;
|
||||
title: string;
|
||||
author: string;
|
||||
}
|
||||
|
||||
export async function getPosts(): Promise<Post[]> {
|
||||
try {
|
||||
const resp = await axios.get(url);
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -35,3 +35,13 @@ test("Increment button functions correctly", async t => {
|
||||
await t.click(button);
|
||||
await t.expect(button.textContent).contains("1");
|
||||
});
|
||||
|
||||
fixture`Posts from API`.page("http://localhost:3000");
|
||||
|
||||
test("At least one post exists", async t => {
|
||||
/**
|
||||
* Test if the API responds with at least one post and it is rendered to the page
|
||||
*/
|
||||
const post = Selector(".post");
|
||||
await t.expect(post.exists).ok();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user