From 6b4acb5913cf347e13343ce7a48b63d1bcd762b1 Mon Sep 17 00:00:00 2001 From: rick Date: Fri, 21 Oct 2022 03:15:09 +0200 Subject: [PATCH] feat(front): add tests for frontend --- .gitignore | 198 +++++++++++++++++++++- README.md | 31 +++- front/Cargo.toml | 17 ++ front/Trunk.toml | 2 + front/assets/style.scss | 0 front/index.html | 9 + front/main.js | 7 + front/package.json | 11 ++ front/src/main.rs | 363 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 628 insertions(+), 10 deletions(-) create mode 100644 front/Cargo.toml create mode 100644 front/Trunk.toml create mode 100644 front/assets/style.scss create mode 100644 front/index.html create mode 100644 front/main.js create mode 100644 front/package.json create mode 100644 front/src/main.rs diff --git a/.gitignore b/.gitignore index 7d5ea64..153c255 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,58 @@ .env -# Created by https://www.toptal.com/developers/gitignore/api/vim,go,rust -# Edit at https://www.toptal.com/developers/gitignore?templates=vim,go,rust +# Created by https://www.toptal.com/developers/gitignore/api/node,go,vim,rust,emacs +# Edit at https://www.toptal.com/developers/gitignore?templates=node,go,vim,rust,emacs + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + ### Go ### # If you prefer the allow list template instead of the deny list, see community template: @@ -30,6 +81,146 @@ go.work /vendor/ /Godeps/ +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + ### Rust ### # Generated by Cargo # will have compiled files and executables @@ -61,10 +252,9 @@ Sessionx.vim # Temporary .netrwhist -*~ # Auto-generated tag files tags # Persistent undo [._]*.un~ -# End of https://www.toptal.com/developers/gitignore/api/vim,go,rust +# End of https://www.toptal.com/developers/gitignore/api/node,go,vim,rust,emacs diff --git a/README.md b/README.md index 5a80a20..87919eb 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,42 @@ * routes : configuration des routes * views : fichiers html dynamiques * dao : fichiers pour faire des requetes en BDD - * models : structures utilisées pour manipuler le contenu de la base de données + * models : structures utilisées pour manipuler le contenu de la base de + données ## Lancement +### Frontend + +Le frontend utilise **Rust** et **WASM**. Il vous faut Trunk en plus de Cargo +pour pouvoir compiler les fichiers Rust en WASM. Il faut en premier installer +les dépendances JS puis compiler les fichiers avec cargo puis Trunk. + +``` +cd front +npm install +cargo build +trunk build +``` + +JS est nécessaire pour une crate qui permet d'utiliser les propriétés d'une +bibliothèque JS. + ### Backend -Il faut mettre en place une base de données [MongoDB](https://www.mongodb.com/). -Il est nécessaire d'avoir [une application Discord](https://discord.com/developers/) -et [une clé d'API Steam](https://steamcommunity.com/dev). -Configurez ensuite le fichier `example.env` et renommez le en `.env`. +Il faut mettre en place une base de données +[MongoDB](https://www.mongodb.com/). Il est nécessaire d'avoir [une +application Discord](https://discord.com/developers/) et [une clé d'API +Steam](https://steamcommunity.com/dev). Configurez ensuite le fichier +`example.env` et renommez le en `.env`. Lancez le serveur avec `go run main.go`. ### Documentation Installer swager avec Go et lancer le. Vous pouvez démarrer le serveur et aller -sur le lien [de la documentation](localhost:8080/swagger) pour pouvoir la lire en ligne. +sur le lien [de la documentation](localhost:8080/swagger) pour pouvoir la lire +en ligne. ``` go install github.com/swaggo/swag diff --git a/front/Cargo.toml b/front/Cargo.toml new file mode 100644 index 0000000..41aeb02 --- /dev/null +++ b/front/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "front" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +reqwest = { version = "0.11.12", features = ["blocking", "json"] } + +[dependencies] +yew = "0.19.3" +yew-router = "0.16" +gloo-net = "0.2" +serde = { version = "1.0", features = ["derive"] } +wasm-bindgen-futures = "0.4" +gloo-storage = "0.2.2" + +patternfly-yew = "0.2.0" diff --git a/front/Trunk.toml b/front/Trunk.toml new file mode 100644 index 0000000..02abfe5 --- /dev/null +++ b/front/Trunk.toml @@ -0,0 +1,2 @@ +[build] +dist = "../dist" diff --git a/front/assets/style.scss b/front/assets/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..8e8a004 --- /dev/null +++ b/front/index.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/front/main.js b/front/main.js new file mode 100644 index 0000000..dd83079 --- /dev/null +++ b/front/main.js @@ -0,0 +1,7 @@ +import './node_modules/@patternfly/patternfly/patternfly.scss'; +import './node_modules/@patternfly/patternfly/patternfly-addons.scss'; +import './static/style.scss'; + +import("./pkg").then(module => { + module.main(); +}); diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..6e1173d --- /dev/null +++ b/front/package.json @@ -0,0 +1,11 @@ +{ + "name": "patternfly-yew", + "version": "0.0.1", + "private": true, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@patternfly/patternfly": "^4.159.1" + } +} diff --git a/front/src/main.rs b/front/src/main.rs new file mode 100644 index 0000000..d5f7994 --- /dev/null +++ b/front/src/main.rs @@ -0,0 +1,363 @@ +use gloo_net::http::Request; +use gloo_net::Error; +use gloo_storage::errors::StorageError; +use gloo_storage::{LocalStorage, Storage}; +use patternfly_yew::{Toast, ToastDispatcher, ToastViewer, Type}; +use serde::{Deserialize, Serialize}; +use yew::prelude::*; +use yew_router::prelude::*; + +#[derive(Clone, Deserialize, PartialEq, Serialize)] +struct User { + id: String, + banned: bool, + blacklisted: bool, + experience: u32, + level: u32, + money: u32, + moneylimit: u32, + discordname: String, +} + +#[derive(Clone, Routable, PartialEq)] +enum Route { + #[at("/")] + Home, + #[at("/about")] + About, + #[at("/test")] + Test, + #[at("/404")] + NotFound, +} + +#[function_component(About)] +fn about() -> Html { + let _history = use_history().unwrap(); + //LocalStorage::set("test", "coucou"); + let test: String = LocalStorage::get("test").unwrap_or(String::from("nop")); + //let onclick = Callback::once(move |_| history.push(Route::Home)); + html! { + <> +

{ "À propos" }

+

{ test }

+ //{ "Index" } + + } +} + +struct Home { + value: String, + user: Option, +} + +#[derive(PartialEq)] +enum TestMessage { + Test, + Toast(ToastType), + ConnexionOk(User), +} + +#[derive(PartialEq)] +struct ToastFields { + pub title: String, + pub body: String, +} + +#[derive(PartialEq)] +enum ToastType { + Info(ToastFields), + Error(ToastFields), +} + +impl Component for Home { + type Message = TestMessage; + type Properties = (); + + fn create(_ctx: &Context) -> Self { + Self { + value: String::from("coucou"), + user: None, + } + } + + fn view(&self, ctx: &Context) -> Html { + let user: Result = LocalStorage::get("auth"); + if let Ok(user) = user { + if let None = self.user { + ctx.link().send_message(TestMessage::ConnexionOk(user)); + } + } else { + ctx.link().send_future_batch(async { + match Request::get("/api/users/current").send().await { + Ok(resp) => { + if resp.ok() { + let json: Result = resp.json().await; + match json { + Ok(json) => { + let user = json.clone(); + match LocalStorage::set("auth", json) { + Ok(_) => vec![ + TestMessage::Toast(ToastType::Info(ToastFields { + title: String::from("Connexion réussie !"), + body: String::new(), + })), + TestMessage::ConnexionOk(user), + ], + Err(err) => vec![TestMessage::Toast(ToastType::Error( + ToastFields { + title: String::from("Error from the promise"), + body: err.to_string(), + }, + ))], + } + } + Err(err) => { + LocalStorage::delete("auth"); + vec![TestMessage::Toast(ToastType::Error(ToastFields { + title: String::from("Error from the promise"), + body: err.to_string(), + }))] + } + } + } else { + vec![TestMessage::Toast(ToastType::Error(ToastFields { + title: String::from("Error from the promise"), + body: String::from("L'API ne retourne pas un code 200."), + }))] + } + } + Err(err) => vec![TestMessage::Toast(ToastType::Error(ToastFields { + title: String::from("Error from the promise"), + body: err.to_string(), + }))], + } + }); + } + + let onclick = ctx.link().callback(|_| TestMessage::Test); + + html! { + <> + + if let Some(user) = &self.user { +

{ format!("Bienvenue {} !", user.discordname) }

+ } else { +

{ "SE CONNECTER VIA DISCORD ICI" }

+

{ "SE CONNECTER VIA STEAM ICI" }

+ } + + + } + } + + fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { + match msg { + TestMessage::ConnexionOk(user) => { + self.user = Some(user); + return true; + } + TestMessage::Toast(toast) => match toast { + ToastType::Info(infos) => ToastDispatcher::new().toast(Toast { + title: infos.title, + r#type: Type::Info, + ..Default::default() + }), + ToastType::Error(infos) => ToastDispatcher::new().toast(Toast { + title: infos.title, + r#type: Type::Danger, + body: html! {

{ infos.body }

}, + ..Default::default() + }), + }, + //_ => ToastDispatcher::new().toast(html! {}), + _ => ToastDispatcher::new().toast(Toast { + title: String::from("Un callback n'a pas été implémenté !"), + r#type: Type::Warning, + ..Default::default() + }), + } + + false + /* + if msg == TestMessage::Test { + self.value = String::from("nouveau"); + true + } else if let TestMessage::StatusConnection(_) = msg { + self.value = String::from("yes ça marche"); + true + } else { + false + } + */ + } +} + +/* +#[function_component(Home)] +fn home() -> Html { + // check if the cookie is set + let state = use_state(|| StatusConnection::NotConnected); + //let connected = use_state_eq(|| false); + { + let state = state.clone(); + use_effect(move || { + //let connected = connected.clone(); + wasm_bindgen_futures::spawn_local(async move { + if let Ok(resp) = Request::get("/api/users/current").send().await { + if resp.ok() { + let json: Result = resp.json().await; + if let Ok(json) = json { + LocalStorage::set("auth", json); + state.set(StatusConnection::UpdateConnection); + } else if let Err(err) = json { + LocalStorage::delete("auth"); + state.set(StatusConnection::Error(err.to_string())); + } + } else { + state.set(StatusConnection::NotConnected); + } + } + }); + || () + }); + } + + /* + let client = Client::new(); + if let Ok(resp) = client.get("/users/current").send() { + if resp.status() == StatusCode::OK { + if let Ok(json) = resp.json::() { + LocalStorage::set("auth", json); + } + } + } + */ + + let _user: Result = LocalStorage::get("auth"); + if let Ok(user) = LocalStorage::get("auth") { + if *state == StatusConnection::UpdateConnection { + let state = state.clone(); + state.set(StatusConnection::Connected(user)); + } + } + + + match &*state { + StatusConnection::NotConnected => { + html! { + <> +

{ "SE CONNECTER VIA DISCORD ICI" }

+

{ "SE CONNECTER VIA STEAM ICI" }

+ + } + }, + StatusConnection::UpdateConnection => { + html!{

{ "Connexion en cours..." }

} + }, + StatusConnection::Connected(user) => html!{

{ format!("Bienvenue {} !", user.discordname) }

}, + StatusConnection::Error(err) => { + html! { + <> +

{ "Erreur !!" }

+

{ err }

+ + } + } + } + + /* + let tmp: Result = LocalStorage::get("auth"); + if let Ok(o) = tmp { + } else { + let check_auth = use_state(|| None::); + + wasm_bindgen_futures::spawn_local(async move { + if let Ok(resp) = Request::get("/users/current").send().await { + if let Ok(user) = resp.json().await { + check_auth.set(Some(user)) + } + }; + }); + + if let Some(user) = (*check_auth).clone() { + LocalStorage::set("auth", user); + + } + + //html! { "Vous êtes connecté !" } + } + */ +} +*/ + +fn switch(route: &Route) -> Html { + match route { + Route::Home => html! { }, + Route::About => html! { }, + Route::NotFound => html! {

{"404, déso"}

}, + Route::Test => html! { }, + } +} + +#[function_component(App)] +fn app() -> Html { + html! { + + render={Switch::render(switch)} /> + + } + /* + html! { +
+

{ "Chasseurs De Succès" }

+

{ "Bienvenue sur le site pour gérer les groupes de chasses" }

+
+ } + */ +} + +fn main() { + yew::start_app::(); +} + +/* +#[derive(Clone, Deserialize, PartialEq, Serialize)] +struct Group { + name: String, +} + +struct ListGroups { + list: Vec, +} + +impl Component for ListGroups { + fn create(_ctx: &Context) -> Self { + Self { + + } + } +} +*/ + +#[function_component(Test)] +fn test() -> Html { + let test = use_state(|| String::from("")); + { + let test = test.clone(); + wasm_bindgen_futures::spawn_local(async move { + if let Ok(o) = Request::get("/test").send().await { + test.set(o.text().await.unwrap()) + } else { + test.set(String::from("Pas de connexion !!")) + }; + }); + } + + html! { + <> +

{ "Moi !!!" }

+

{ (*test).clone() }

+ + } +}