From 4a4fd60b55f2c02cae761abfa7085daa3dd63076 Mon Sep 17 00:00:00 2001 From: wawa Date: Tue, 30 May 2023 10:40:02 +0200 Subject: [PATCH] Michal Wawryszczuk challenge --- package.json | 3 + src/App.vue | 88 +++++++++++++++++++++++---- src/assets/base.scss | 25 ++++++++ src/assets/icomoon/icomoon.ttf | Bin 0 -> 1300 bytes src/assets/icomoon/icomoon.woff | Bin 0 -> 1376 bytes src/assets/icomoon/selection.json | 1 + src/assets/iconVariables.scss | 6 ++ src/assets/icons.scss | 40 ++++++++++++ src/assets/main.css | 0 src/assets/main.scss | 5 ++ src/components/StrengthIndicator.vue | 66 ++++++++++++++++++++ src/customTests/Password.spec.js | 29 +++++++++ src/main.js | 2 +- src/stores/strong-password.js | 68 ++++++++++++++++++++- vite.config.js | 8 +++ 15 files changed, 326 insertions(+), 15 deletions(-) create mode 100644 src/assets/base.scss create mode 100644 src/assets/icomoon/icomoon.ttf create mode 100644 src/assets/icomoon/icomoon.woff create mode 100644 src/assets/icomoon/selection.json create mode 100644 src/assets/iconVariables.scss create mode 100644 src/assets/icons.scss delete mode 100644 src/assets/main.css create mode 100644 src/assets/main.scss create mode 100644 src/components/StrengthIndicator.vue create mode 100644 src/customTests/Password.spec.js diff --git a/package.json b/package.json index 9e84a2c..086e80f 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "eslint": "^8.22.0", "eslint-plugin-vue": "^9.3.0", "jsdom": "^20.0.3", + "node-sass": "^9.0.0", "prettier": "^2.7.1", + "sass": "^1.62.1", + "sass-loader": "^13.3.1", "vite": "^3.2.4", "vitest": "^0.25.3" } diff --git a/src/App.vue b/src/App.vue index 07d8229..8b5c326 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,26 +1,90 @@ - + diff --git a/src/assets/base.scss b/src/assets/base.scss new file mode 100644 index 0000000..27bf672 --- /dev/null +++ b/src/assets/base.scss @@ -0,0 +1,25 @@ +//VARIABLES +$color-red: #c70000; +$color-green: #008000; +$color-yellow: #ffc300; +$color-lightgray: #d3d3d3; +$color-background: #f4f4f4; +$color-indicator: #cbcbcb; + + +//ANIMATIONS +.reduce-enter-from, +.reduce-leave-to { + transform: scale(0); +} + +.reduce-enter-to, +.reduce-leave-from { + transform: scale(1); +} + +.reduce-move, +.reduce-enter-active, +.reduce-leave-active { + transition: transform 0.3s ease-in-out; +} \ No newline at end of file diff --git a/src/assets/icomoon/icomoon.ttf b/src/assets/icomoon/icomoon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7cdc5ce3932d4fed1662069c52214b92a89a63f1 GIT binary patch literal 1300 zcmaJ=O-NKx7(I91{5WHb5$Z&UKGK>NF*<6Fl9emTN-QlgLi0KE8b@a44f6vU5w0R~ zQCnNLXca`;7DGV=DTLB82!ue|)+%f|_r0q#q4hoPz2`gM-+6x!0P3*}4P9dso!t#b zJj-NWbIryxMlLkD@emL#^sgt4LXPKQ`tqzxrkCyLaor(bJ=vj4UrU|VSAN*O~M7lZOy?3!)^ zDO|1=#+*~Lvs&OrKluRa;Nm5qZ;Q%KrD9j<+pw{yY=zvctL-ZwnAK3jxq#S!ol?#F zKgVdL%*JJ$mMTQQh#vO^h0og*C2S0}icoV;zZekRvGXFqVn!Rh~{c$Jv^OQEpPU&`6?*>QDVn`*z^F!&SoZpq^7)1hzGQ$00) zyHcxdtg*iVzEEl0t8c+(;B_L7Bg>?}#|6-cRxIE-Nm=Q|fa<+Ff#jYA@j~?%@R4<9 zxrk>K)`ZZnUIeWNQz#&X_}5b-DnUUB#(+H9XIs+tk^V$5#&|MB z4tf;N9z2;CFCIKJYV;r`2IA4ggv5k+_GGYrvu|lJR;HQRZ@!tGncaQ8H5QMPAc$+0 zn&cX=ea3&w*hEhsk=O@zmbLkS>s}&fEPzYHzrq@vTwhKZMdn_^=Ump7RCehmxOdP? ztouK`ET>H)Nu&i3A7t&Gct4T`3GO2NZdT75?L#_OS_Frhbdc2#leH3@w+zUxpFY#w zoUw=;?J;~^s+UgYjhqSY6?)!hT|MvpwO|!XIB%?$rvYi&Rq=>iM2=O*8vKX$Dt=JE z@WS6MqGh+oI7YN3v`V!?F%1ev-YHfVU0T@%s2wrJ?XaBHPEH!a>cdSr@DrqOiRyN> z3J!a>NMcdl3_8^g8GsQakku@!*qnnXIxJh)`|r&mTCJdPg^u$SVn{>|d;G%VZjAs2 zf*m5*);}bMMPKxchz_1$I1mbp(9y0)r|<+lAx|h8iUu7tKRvT@46-saJ--`&y#8t9 z+WMNbTIJuko%>}m#cRGNqiJzXU`hq-Q?OqPxS%o#=L4}>bWrbb%r?)EX27f_e zA046x{Ea!iVEl;K*a4hUSOY$-uoL)>!Y*nfU12x)afN*}Ku(rFa4$`gNd+n*0RhyBP?&lf?rKPmo2hcvD4?l{LCcgSg9;5Z<5s?;r_8)r zFiK`ppIg#L64oeY3sYS7E}?r0W%6<&gGyMF;O1xvc0?Xv)II=a#Dmw~qapYM_Bq+x literal 0 HcmV?d00001 diff --git a/src/assets/icomoon/selection.json b/src/assets/icomoon/selection.json new file mode 100644 index 0000000..a116d35 --- /dev/null +++ b/src/assets/icomoon/selection.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M512.006-0.008c-0.003-0-0.006-0-0.010-0-282.771 0-512.003 229.23-512.005 512.001l-0 0c-0 0.003-0 0.006-0 0.010 0 282.773 229.232 512.005 512.005 512.005 0.003 0 0.006-0 0.009-0l0-0c282.771-0.002 512.002-229.234 512.002-512.005 0-0.003-0-0.007-0-0.010l0 0.001c-0.002-282.77-229.232-512-512.002-512.002l0.001 0zM714.755 212.512l90.768 88.145-204.034 210.064 210.013 204.034-88.145 90.768-210.064-204.034-204.034 210.013-90.781-88.196 204.047-210.013-210.026-204.034 88.158-90.768 210.051 204.034z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["notPassedIcon"]},"attrs":[{}],"properties":{"order":2,"id":1,"name":"notPassedIcon","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M512-0.001c-282.77 0.002-512 229.232-512.002 512.002l0-0.001c0.002 282.77 229.232 512 512.002 512.002l-0-0c282.77-0.002 512-229.232 512.002-512.002l-0 0.001c-0.002-282.77-229.232-512-512.002-512.002l0 0zM738.572 194.673l99.947 77.551-432.316 557.103-220.709-319.435 104.087-71.92 122.549 177.357z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["passedIcon"]},"attrs":[{}],"properties":{"order":3,"id":0,"name":"passedIcon","prevSize":32,"code":59649},"setIdx":0,"setId":2,"iconIdx":1}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"cssVars":true,"cssVarsFormat":"scss","showSelector":false,"showMetrics":false,"showMetadata":false,"showVersion":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16}} \ No newline at end of file diff --git a/src/assets/iconVariables.scss b/src/assets/iconVariables.scss new file mode 100644 index 0000000..e6d9299 --- /dev/null +++ b/src/assets/iconVariables.scss @@ -0,0 +1,6 @@ +$icomoon-font-family: "icomoon"; +$icomoon-font-path: "src/assets/icomoon"; + +$icon-notPassedIcon: "\e900"; +$icon-passedIcon: "\e901"; + diff --git a/src/assets/icons.scss b/src/assets/icons.scss new file mode 100644 index 0000000..c29017e --- /dev/null +++ b/src/assets/icons.scss @@ -0,0 +1,40 @@ +@import './iconVariables.scss'; + +@font-face { + font-family: '#{$icomoon-font-family}'; + src: url('#{$icomoon-font-path}/#{$icomoon-font-family}.eot?pfwaw0'); + src: url('#{$icomoon-font-path}/#{$icomoon-font-family}.eot?pfwaw0#iefix') format('embedded-opentype'), + url('#{$icomoon-font-path}/#{$icomoon-font-family}.ttf?pfwaw0') format('truetype'), + url('#{$icomoon-font-path}/#{$icomoon-font-family}.woff?pfwaw0') format('woff'), + url('#{$icomoon-font-path}/#{$icomoon-font-family}.svg?pfwaw0##{$icomoon-font-family}') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: '#{$icomoon-font-family}' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-notPassedIcon { + &:before { + content: $icon-notPassedIcon; + } +} +.icon-passedIcon { + &:before { + content: $icon-passedIcon; + } +} + diff --git a/src/assets/main.css b/src/assets/main.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/assets/main.scss b/src/assets/main.scss new file mode 100644 index 0000000..2dc7a30 --- /dev/null +++ b/src/assets/main.scss @@ -0,0 +1,5 @@ +@import './icons'; + +body { + background-color: $color-background; +} \ No newline at end of file diff --git a/src/components/StrengthIndicator.vue b/src/components/StrengthIndicator.vue new file mode 100644 index 0000000..ee361bc --- /dev/null +++ b/src/components/StrengthIndicator.vue @@ -0,0 +1,66 @@ + + + + + \ No newline at end of file diff --git a/src/customTests/Password.spec.js b/src/customTests/Password.spec.js new file mode 100644 index 0000000..9758914 --- /dev/null +++ b/src/customTests/Password.spec.js @@ -0,0 +1,29 @@ +import { describe, it, beforeEach, expect } from "vitest"; +import { createPinia, setActivePinia } from "pinia/dist/pinia"; +import { useStrongPasswordStore } from "@/stores/strong-password"; + +describe('Password', () => { + beforeEach(() => { + setActivePinia(createPinia()) + }); + + it('At least one letter', () => { + const strongPasswordStore = useStrongPasswordStore(); + const oneLetter = strongPasswordStore.rules.find(rule => rule.name === 'OneLetter'); + + const expectTruthy = ['a', 'B', 'abc', '1a2s', 'AAbb', 'Mot%!']; + const expectFalsy = ['', 1, '%', '1@3']; + + expectTruthy.forEach(passwordCanidate => { + strongPasswordStore.password = passwordCanidate; + strongPasswordStore.validateInput(); + expect(oneLetter.isCorrect).toBeTruthy(); + }) + + expectFalsy.forEach(passwordCanidate => { + strongPasswordStore.password = passwordCanidate; + strongPasswordStore.validateInput(); + expect(oneLetter.isCorrect).toBeFalsy(); + }) + }) +}) \ No newline at end of file diff --git a/src/main.js b/src/main.js index 6641b86..1594b74 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,7 @@ import { createApp } from "vue"; import { createPinia } from "pinia"; import App from "./App.vue"; -import "./assets/main.css"; +import "./assets/main.scss"; const app = createApp(App); diff --git a/src/stores/strong-password.js b/src/stores/strong-password.js index adefaef..98107cf 100644 --- a/src/stores/strong-password.js +++ b/src/stores/strong-password.js @@ -1,5 +1,69 @@ import { defineStore } from "pinia"; -export const useStrongPasswordStore = defineStore("strong_password", () => { - return {}; +export const useStrongPasswordStore = defineStore("strong_password", { + state: () => ({ + password: '', + rules: [ + { + name: 'OneLetter', + pattern: /[a-zA-z]/, + label: 'Has at least one letter', + isCorrect: false + }, + { + name: 'UpperAndLower', + pattern: /(?=.*[a-z])(?=.*[A-Z])/, + label: 'Has at least one lower and one upper case letter', + isCorrect: false + }, + { + name: 'OneNumber', + pattern: /\d/, + label: 'Has at least one number', + isCorrect: false + }, + { + name: 'SpecialSymbol', + pattern: /[^\w]/, + label: 'Has at least one special character', + isCorrect: false + }, + { + name: 'LongerThan4', + pattern: /.{5,}/, + label: 'Has length > 4', + isCorrect: false + }, + { + name: 'LongerThan8', + pattern: /.{9,}/, + label: 'Has length > 8', + isCorrect: false + }, + { + name: 'LongerThan12', + pattern: /.{13,}/, + label: 'Has length > 12', + isCorrect: false + }, + ] + }), + + getters: { + countPassedRules: (state) => state.rules.filter(rule => rule.isCorrect).length, + isPasswordStrong() { + return this.countPassedRules >= 5; + }, + isPasswordWeak() { + return !!this.countPassedRules && this.countPassedRules < 5; + } + }, + + actions: { + validateInput() { + this.rules.forEach(rule => { + rule.pattern.test(this.password) ? rule.isCorrect = true : rule.isCorrect = false; + }); + } + } }); diff --git a/vite.config.js b/vite.config.js index 4d60b3a..7ef3850 100644 --- a/vite.config.js +++ b/vite.config.js @@ -11,4 +11,12 @@ export default defineConfig({ "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, + css: { + preprocessorOptions: { + scss: { + additionalData: ` + @import "./src/assets/base.scss";` + } + } +} });