Merge pull request 'salix-front-js' (#5) from salix-front-js into dev
Reviewed-on: #5
This commit is contained in:
commit
909756a1cc
8
.babelrc
8
.babelrc
|
@ -1,12 +1,8 @@
|
||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": ["@babel/plugin-syntax-dynamic-import"],
|
||||||
"@babel/plugin-syntax-dynamic-import"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"test": {
|
"test": {
|
||||||
"plugins": [
|
"plugins": ["dynamic-import-node"],
|
||||||
"dynamic-import-node"
|
|
||||||
],
|
|
||||||
"presets": [
|
"presets": [
|
||||||
[
|
[
|
||||||
"@babel/preset-env",
|
"@babel/preset-env",
|
||||||
|
|
|
@ -3,7 +3,7 @@ root = true
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
|
@ -6,4 +6,3 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
babel.config.js
|
babel.config.js
|
||||||
/src-ssr
|
|
39
.eslintrc.js
39
.eslintrc.js
|
@ -1,21 +1,11 @@
|
||||||
const { resolve } = require('path');
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||||
// This option interrupts the configuration hierarchy at this file
|
// This option interrupts the configuration hierarchy at this file
|
||||||
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
// https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
|
|
||||||
// Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working
|
|
||||||
// `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#configuration
|
parser: '@babel/eslint-parser',
|
||||||
// https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#eslint
|
|
||||||
// Needed to make the parser take into account 'vue' files
|
|
||||||
extraFileExtensions: ['.vue'],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
project: resolve(__dirname, './tsconfig.json'),
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||||
sourceType: 'module', // Allows for the use of imports
|
sourceType: 'module', // Allows for the use of imports
|
||||||
},
|
},
|
||||||
|
@ -27,18 +17,12 @@ module.exports = {
|
||||||
// Rules order is important, please avoid shuffling them
|
// Rules order is important, please avoid shuffling them
|
||||||
extends: [
|
extends: [
|
||||||
// Base ESLint recommended rules
|
// Base ESLint recommended rules
|
||||||
// 'eslint:recommended',
|
'eslint:recommended',
|
||||||
|
|
||||||
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
|
|
||||||
// ESLint typescript rules
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
// consider disabling this class of rules if linting takes too long
|
|
||||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
|
||||||
|
|
||||||
// Uncomment any of the lines below to choose desired strictness,
|
// Uncomment any of the lines below to choose desired strictness,
|
||||||
// but leave only one uncommented!
|
// but leave only one uncommented!
|
||||||
// See https://eslint.vuejs.org/rules/#available-rules
|
// See https://eslint.vuejs.org/rules/#available-rules
|
||||||
// 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
//'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||||
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||||
|
|
||||||
|
@ -48,9 +32,6 @@ module.exports = {
|
||||||
],
|
],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
// required to apply rules which need type information
|
|
||||||
'@typescript-eslint',
|
|
||||||
|
|
||||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||||
// required to lint *.vue files
|
// required to lint *.vue files
|
||||||
'vue',
|
'vue',
|
||||||
|
@ -71,25 +52,19 @@ module.exports = {
|
||||||
process: 'readonly',
|
process: 'readonly',
|
||||||
Capacitor: 'readonly',
|
Capacitor: 'readonly',
|
||||||
chrome: 'readonly',
|
chrome: 'readonly',
|
||||||
|
defineProps: 'readonly', // Vue SFC setup compiler macro
|
||||||
|
defineEmits: 'readonly', // Vue SFC setup compiler macro
|
||||||
|
defineExpose: 'readonly', // Vue SFC setup compiler macro
|
||||||
},
|
},
|
||||||
|
|
||||||
// add your custom rules here
|
// add your custom rules here
|
||||||
rules: {
|
rules: {
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
|
||||||
// TypeScript
|
|
||||||
quotes: ['warn', 'single', { avoidEscape: true }],
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/unbound-method': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-call': 'off',
|
|
||||||
'@typescript-eslint/no-floating-promises': 'off',
|
|
||||||
|
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
},
|
},
|
||||||
|
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['**/*.spec.{js,ts}'],
|
files: ['**/*.spec.{js,ts}'],
|
||||||
|
|
|
@ -26,7 +26,6 @@ yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode
|
|
||||||
.idea
|
.idea
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 120,
|
|
||||||
"tabWidth": 4,
|
|
||||||
"semi": true,
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
module.exports = {
|
||||||
|
singleQuote: true,
|
||||||
|
printWidth: 120,
|
||||||
|
tabWidth: 4,
|
||||||
|
semi: true,
|
||||||
|
endOfLine: 'auto',
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"johnsoncodehk.volar",
|
||||||
|
"wayou.vscode-todo-highlight"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"octref.vetur",
|
||||||
|
"hookyqr.beautify",
|
||||||
|
"dbaeumer.jshint",
|
||||||
|
"ms-vscode.vscode-typescript-tslint-plugin"
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,6 +2,17 @@
|
||||||
"editor.bracketPairColorization.enabled": true,
|
"editor.bracketPairColorization.enabled": true,
|
||||||
"editor.guides.bracketPairs": true,
|
"editor.guides.bracketPairs": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "johnsoncodehk.volar",
|
||||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"]
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
|
||||||
|
"jest.jestCommandLine": "jest",
|
||||||
|
"json.schemas": [
|
||||||
|
{
|
||||||
|
"fileMatch": ["cypress.json"],
|
||||||
|
"url": "https://on.cypress.io/cypress.schema.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ Salix front-end
|
||||||
## Install the dependencies
|
## Install the dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
yarn
|
||||||
|
# or
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -17,12 +19,16 @@ quasar dev
|
||||||
### Lint the files
|
### Lint the files
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
yarn lint
|
||||||
|
# or
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Format the files
|
### Format the files
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
yarn format
|
||||||
|
# or
|
||||||
npm run format
|
npm run format
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -34,4 +40,4 @@ quasar build
|
||||||
|
|
||||||
### Customize the configuration
|
### Customize the configuration
|
||||||
|
|
||||||
See [Configuring quasar.conf.js](https://quasar.dev/quasar-cli/quasar-conf-js).
|
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js).
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const esModules = ['quasar', 'quasar/lang', 'lodash-es'].join('|');
|
const esModules = ['quasar', 'quasar/lang', 'lodash-es'].join('|');
|
||||||
|
|
||||||
/* eslint-env node */
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
globals: {
|
globals: {
|
||||||
__DEV__: true,
|
__DEV__: true,
|
||||||
|
@ -9,12 +8,9 @@ module.exports = {
|
||||||
'vue-jest': {
|
'vue-jest': {
|
||||||
pug: { doctype: 'html' },
|
pug: { doctype: 'html' },
|
||||||
},
|
},
|
||||||
// Remove if using `const enums`
|
|
||||||
// See https://huafu.github.io/ts-jest/user/config/isolatedModules#example
|
|
||||||
'ts-jest': {
|
|
||||||
isolatedModules: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
// Jest assumes we are testing in node environment, specify jsdom environment instead
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
// noStackTrace: true,
|
// noStackTrace: true,
|
||||||
// bail: true,
|
// bail: true,
|
||||||
// cache: false,
|
// cache: false,
|
||||||
|
@ -22,13 +18,8 @@ module.exports = {
|
||||||
// watch: true,
|
// watch: true,
|
||||||
collectCoverage: false,
|
collectCoverage: false,
|
||||||
coverageDirectory: '<rootDir>/test/jest/coverage',
|
coverageDirectory: '<rootDir>/test/jest/coverage',
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: ['<rootDir>/src/**/*.vue', '<rootDir>/src/**/*.js', '<rootDir>/src/**/*.jsx'],
|
||||||
'<rootDir>/src/**/*.vue',
|
// Needed in JS codebases too because of feature flags
|
||||||
'<rootDir>/src/**/*.js',
|
|
||||||
'<rootDir>/src/**/*.ts',
|
|
||||||
'<rootDir>/src/**/*.jsx',
|
|
||||||
'<rootDir>/src/**/*.tsx',
|
|
||||||
],
|
|
||||||
coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$'],
|
coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$'],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
|
@ -38,27 +29,14 @@ module.exports = {
|
||||||
// statements: 50
|
// statements: 50
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
testMatch: [
|
testMatch: ['<rootDir>/src/**/__tests__/*.(spec|test).+(ts|js)?(x)'],
|
||||||
// Matches tests in any subfolder of 'src' or into 'test/jest/__tests__'
|
moduleFileExtensions: ['vue', 'js', 'jsx', 'json'],
|
||||||
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
|
|
||||||
// '<rootDir>/test/jest/__tests__/**/*.(spec|test).+(ts|js)?(x)',
|
|
||||||
'<rootDir>/src/**/__tests__/*.(spec|test).+(ts|js)?(x)',
|
|
||||||
],
|
|
||||||
// Extension-less imports of components are resolved to .ts files by TS,
|
|
||||||
// grating correct type-checking in test files.
|
|
||||||
// Being 'vue' the first moduleFileExtension option, the very same imports
|
|
||||||
// will be resolved to .vue files by Jest, if both .vue and .ts files are
|
|
||||||
// in the same folder.
|
|
||||||
// This guarantee a great dev experience both for testing and type-checking.
|
|
||||||
// See https://github.com/vuejs/vue-jest/issues/188#issuecomment-620750728
|
|
||||||
moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'],
|
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^quasar$': 'quasar/dist/quasar.esm.prod.js',
|
'^quasar$': 'quasar/dist/quasar.esm.prod.js',
|
||||||
'^~/(.*)$': '<rootDir>/$1',
|
'^~/(.*)$': '<rootDir>/$1',
|
||||||
'^src/(.*)$': '<rootDir>/src/$1',
|
'^src/(.*)$': '<rootDir>/src/$1',
|
||||||
'^app/(.*)$': '<rootDir>/$1',
|
'^app/(.*)$': '<rootDir>/$1',
|
||||||
'^components/(.*)$': '<rootDir>/src/components/$1',
|
'^components/(.*)$': '<rootDir>/src/components/$1',
|
||||||
'^composables/(.*)$': '<rootDir>/src/composables/$1',
|
|
||||||
'^layouts/(.*)$': '<rootDir>/src/layouts/$1',
|
'^layouts/(.*)$': '<rootDir>/src/layouts/$1',
|
||||||
'^pages/(.*)$': '<rootDir>/src/pages/$1',
|
'^pages/(.*)$': '<rootDir>/src/pages/$1',
|
||||||
'^assets/(.*)$': '<rootDir>/src/assets/$1',
|
'^assets/(.*)$': '<rootDir>/src/assets/$1',
|
||||||
|
@ -66,16 +44,13 @@ module.exports = {
|
||||||
'.*css$': '@quasar/quasar-app-extension-testing-unit-jest/stub.css',
|
'.*css$': '@quasar/quasar-app-extension-testing-unit-jest/stub.css',
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
// See https://jestjs.io/docs/en/configuration.html#transformignorepatterns-array-string
|
|
||||||
[`^(${esModules}).+\\.js$`]: 'babel-jest',
|
|
||||||
'^.+\\.(ts|js|html)$': 'ts-jest',
|
|
||||||
// vue-jest uses find-babel-file, which searches by this order:
|
|
||||||
// (async) .babelrc, .babelrc.js, package.json, babel.config.js
|
|
||||||
// (sync) .babelrc, .babelrc.js, babel.config.js, package.json
|
|
||||||
// https://github.com/tleunen/find-babel-config/issues/33
|
|
||||||
'.*\\.vue$': 'vue-jest',
|
'.*\\.vue$': 'vue-jest',
|
||||||
|
'.*\\.js$': 'babel-jest',
|
||||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||||
|
// use these if NPM is being flaky, care as hosting could interfere with these
|
||||||
|
// '.*\\.vue$': '@quasar/quasar-app-extension-testing-unit-jest/node_modules/vue-jest',
|
||||||
|
// '.*\\.js$': '@quasar/quasar-app-extension-testing-unit-jest/node_modules/babel-jest'
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
|
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
|
||||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
snapshotSerializers: ['jest-serializer-vue'],
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"src/*": ["src/*"],
|
||||||
|
"app/*": ["*"],
|
||||||
|
"components/*": ["src/components/*"],
|
||||||
|
"layouts/*": ["src/layouts/*"],
|
||||||
|
"pages/*": ["src/pages/*"],
|
||||||
|
"assets/*": ["src/assets/*"],
|
||||||
|
"boot/*": ["src/boot/*"],
|
||||||
|
"stores/*": ["src/stores/*"],
|
||||||
|
"vue$": ["node_modules/vue/dist/vue.runtime.esm-bundler.js"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["dist", ".quasar", "node_modules"]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
|
@ -3,41 +3,39 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "Salix front-end",
|
"description": "Salix front-end",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Salix",
|
"author": "Verdnatura",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.ts,.vue ./",
|
"lint": "eslint --ext .js,.vue ./",
|
||||||
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||||
"test:unit:coverage": "jest --coverage",
|
|
||||||
"test:unit": "jest --watchAll",
|
"test:unit": "jest --watchAll",
|
||||||
"test:e2e": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress open --config-file cypress.json\"",
|
"test:unit:ci": "jest --ci",
|
||||||
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\"",
|
"test:unit:coverage": "jest --coverage",
|
||||||
"serve:test:coverage": "quasar serve test/jest/coverage/lcov-report/ --port 8788",
|
"serve:test:coverage": "quasar serve test/jest/coverage/lcov-report/ --port 8788",
|
||||||
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\""
|
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\"",
|
||||||
|
"test:e2e": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress open\"",
|
||||||
|
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.0.0",
|
"@quasar/extras": "^1.0.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"quasar": "^2.0.0",
|
"quasar": "^2.6.0",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vue-i18n": "^9.0.0",
|
"vue-i18n": "^9.0.0",
|
||||||
"vue-router": "^4.0.0"
|
"vue-router": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.13.14",
|
"@babel/eslint-parser": "^7.13.14",
|
||||||
"@intlify/vue-i18n-loader": "^4.1.0",
|
"@quasar/app-webpack": "^3.0.0",
|
||||||
"@quasar/app": "^3.0.0",
|
|
||||||
"@quasar/quasar-app-extension-testing-e2e-cypress": "^4.0.1",
|
"@quasar/quasar-app-extension-testing-e2e-cypress": "^4.0.1",
|
||||||
"@quasar/quasar-app-extension-testing-unit-jest": "^3.0.0-alpha.8",
|
"@quasar/quasar-app-extension-testing-unit-jest": "^3.0.0-alpha.9",
|
||||||
"@types/node": "^12.20.21",
|
"eslint": "^8.10.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
|
||||||
"@typescript-eslint/parser": "^5.10.0",
|
|
||||||
"eslint": "^7.14.0",
|
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint-plugin-jest": "^25.2.2",
|
"eslint-plugin-jest": "^25.2.2",
|
||||||
"eslint-plugin-vue": "^7.0.0",
|
"eslint-plugin-vue": "^8.5.0",
|
||||||
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"eslint-plugin-cypress": "^2.11.3"
|
"eslint-plugin-cypress": "^2.11.3"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,43 +1,38 @@
|
||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
||||||
* the ES6 features that are supported by your Node version. https://node.green/
|
* the ES6 features that are supported by your Node version. https://node.green/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Configuration for your app
|
// Configuration for your app
|
||||||
// https://quasar.dev/quasar-cli/quasar-conf-js
|
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js
|
||||||
|
|
||||||
|
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||||
|
|
||||||
/* eslint-env node */
|
|
||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const { configure } = require('quasar/wrappers');
|
const { configure } = require('quasar/wrappers');
|
||||||
|
|
||||||
module.exports = configure(function (ctx) {
|
module.exports = configure(function (ctx) {
|
||||||
return {
|
return {
|
||||||
// https://quasar.dev/quasar-cli/supporting-ts
|
// https://v2.quasar.dev/quasar-cli-webpack/supporting-ts
|
||||||
supportTS: {
|
supportTS: false,
|
||||||
tsCheckerConfig: {
|
|
||||||
eslint: {
|
|
||||||
enabled: true,
|
|
||||||
files: './src/**/*.{ts,tsx,js,jsx,vue}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/prefetch-feature
|
// https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature
|
||||||
// preFetch: true,
|
// preFetch: true,
|
||||||
|
|
||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://quasar.dev/quasar-cli/boot-files
|
// https://v2.quasar.dev/quasar-cli-webpack/boot-files
|
||||||
boot: ['i18n', 'axios'],
|
boot: ['i18n', 'axios'],
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
|
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
|
||||||
css: ['app.scss'],
|
css: ['app.scss'],
|
||||||
|
|
||||||
// https://github.com/quasarframework/quasar/tree/dev/extras
|
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||||
extras: [
|
extras: [
|
||||||
// 'ionicons-v4',
|
// 'ionicons-v4',
|
||||||
// 'mdi-v5',
|
// 'mdi-v5',
|
||||||
// 'fontawesome-v5',
|
// 'fontawesome-v6',
|
||||||
// 'eva-icons',
|
// 'eva-icons',
|
||||||
// 'themify',
|
// 'themify',
|
||||||
// 'line-awesome',
|
// 'line-awesome',
|
||||||
|
@ -47,7 +42,7 @@ module.exports = configure(function (ctx) {
|
||||||
'material-icons', // optional, you are not bound to it
|
'material-icons', // optional, you are not bound to it
|
||||||
],
|
],
|
||||||
|
|
||||||
// Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
|
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-build
|
||||||
build: {
|
build: {
|
||||||
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||||
|
|
||||||
|
@ -68,14 +63,15 @@ module.exports = configure(function (ctx) {
|
||||||
// Options below are automatically set depending on the env, set them if you want to override
|
// Options below are automatically set depending on the env, set them if you want to override
|
||||||
// extractCSS: false,
|
// extractCSS: false,
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/handling-webpack
|
// https://v2.quasar.dev/quasar-cli-webpack/handling-webpack
|
||||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||||
chainWebpack(/* chain */) {
|
|
||||||
//
|
chainWebpack(chain) {
|
||||||
|
chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: ['js', 'vue'] }]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer
|
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-devServer
|
||||||
devServer: {
|
devServer: {
|
||||||
server: {
|
server: {
|
||||||
type: 'http',
|
type: 'http',
|
||||||
|
@ -92,7 +88,7 @@ module.exports = configure(function (ctx) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework
|
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
|
||||||
framework: {
|
framework: {
|
||||||
config: {},
|
config: {},
|
||||||
|
|
||||||
|
@ -107,14 +103,14 @@ module.exports = configure(function (ctx) {
|
||||||
// directives: [],
|
// directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: ['Notify'],
|
plugins: ['Notify', 'Dialog'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
// https://quasar.dev/options/animations
|
// https://quasar.dev/options/animations
|
||||||
animations: [],
|
animations: [],
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/developing-ssr/configuring-ssr
|
// https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/configuring-ssr
|
||||||
ssr: {
|
ssr: {
|
||||||
pwa: false,
|
pwa: false,
|
||||||
|
|
||||||
|
@ -127,8 +123,8 @@ module.exports = configure(function (ctx) {
|
||||||
maxAge: 1000 * 60 * 60 * 24 * 30,
|
maxAge: 1000 * 60 * 60 * 24 * 30,
|
||||||
// Tell browser when a file from the server should expire from cache (in ms)
|
// Tell browser when a file from the server should expire from cache (in ms)
|
||||||
|
|
||||||
chainWebpackWebserver(/* chain */) {
|
chainWebpackWebserver(chain) {
|
||||||
//
|
chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||||
},
|
},
|
||||||
|
|
||||||
middlewares: [
|
middlewares: [
|
||||||
|
@ -137,21 +133,22 @@ module.exports = configure(function (ctx) {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://quasar.dev/quasar-cli/developing-pwa/configuring-pwa
|
// https://v2.quasar.dev/quasar-cli-webpack/developing-pwa/configuring-pwa
|
||||||
pwa: {
|
pwa: {
|
||||||
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
|
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
|
||||||
workboxOptions: {}, // only for GenerateSW
|
workboxOptions: {}, // only for GenerateSW
|
||||||
|
|
||||||
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
|
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
|
||||||
// if using workbox in InjectManifest mode
|
// if using workbox in InjectManifest mode
|
||||||
chainWebpackCustomSW(/* chain */) {
|
|
||||||
//
|
chainWebpackCustomSW(chain) {
|
||||||
|
chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||||
},
|
},
|
||||||
|
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Salix',
|
name: `Salix`,
|
||||||
short_name: 'Salix',
|
short_name: `Salix`,
|
||||||
description: 'Salix front-end',
|
description: `Salix front-end`,
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait',
|
orientation: 'portrait',
|
||||||
background_color: '#ffffff',
|
background_color: '#ffffff',
|
||||||
|
@ -186,17 +183,17 @@ module.exports = configure(function (ctx) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Full list of options: https://quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova
|
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-cordova-apps/configuring-cordova
|
||||||
cordova: {
|
cordova: {
|
||||||
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||||
},
|
},
|
||||||
|
|
||||||
// Full list of options: https://quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor
|
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-capacitor-apps/configuring-capacitor
|
||||||
capacitor: {
|
capacitor: {
|
||||||
hideSplashscreen: true,
|
hideSplashscreen: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Full list of options: https://quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
|
// Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-electron-apps/configuring-electron
|
||||||
electron: {
|
electron: {
|
||||||
bundler: 'packager', // 'packager' or 'builder'
|
bundler: 'packager', // 'packager' or 'builder'
|
||||||
|
|
||||||
|
@ -218,15 +215,13 @@ module.exports = configure(function (ctx) {
|
||||||
},
|
},
|
||||||
|
|
||||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||||
chainWebpack(/* chain */) {
|
|
||||||
// do something with the Electron main process Webpack cfg
|
chainWebpackMain(chain) {
|
||||||
// extendWebpackMain also available besides this chainWebpackMain
|
chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||||
},
|
},
|
||||||
|
|
||||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
chainWebpackPreload(chain) {
|
||||||
chainWebpackPreload(/* chain */) {
|
chain.plugin('eslint-webpack-plugin').use(ESLintPlugin, [{ extensions: ['js'] }]);
|
||||||
// do something with the Electron main process Webpack cfg
|
|
||||||
// extendWebpackPreload also available besides this chainWebpackPreload
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"@quasar/testing-unit-jest": {
|
"@quasar/testing-unit-jest": {
|
||||||
"babel": "babelrc",
|
"babel": "babelrc",
|
||||||
"options": ["scripts", "typescript"]
|
"options": ["scripts"]
|
||||||
},
|
},
|
||||||
"@quasar/testing-e2e-cypress": {
|
"@quasar/testing-e2e-cypress": {
|
||||||
"options": ["scripts"]
|
"options": ["scripts"]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script lang="ts" setup>
|
<script setup>
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import axios, { AxiosInstance } from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
const { getToken } = useSession();
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
$axios: AxiosInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be careful when using SSR for cross-request state pollution
|
// Be careful when using SSR for cross-request state pollution
|
||||||
// due to creating a Singleton instance here;
|
// due to creating a Singleton instance here;
|
||||||
|
@ -17,6 +10,7 @@ declare module '@vue/runtime-core' {
|
||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({ baseURL: 'https://api.example.com' });
|
const api = axios.create({ baseURL: 'https://api.example.com' });
|
||||||
|
const { getToken } = useSession();
|
||||||
|
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
function (context) {
|
function (context) {
|
|
@ -1,6 +1,5 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import messages from 'src/i18n';
|
import messages from 'src/i18n';
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
|
@ -1,3 +1,19 @@
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import UserPanel from 'src/components/UserPanel.vue';
|
||||||
|
|
||||||
|
const session = useSession();
|
||||||
|
const state = useState();
|
||||||
|
const user = state.getUser();
|
||||||
|
const token = session.getToken();
|
||||||
|
|
||||||
|
defineEmits(['toggle-drawer']);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-header class="bg-dark" color="white" elevated bordered>
|
<q-header class="bg-dark" color="white" elevated bordered>
|
||||||
<q-toolbar class="q-py-sm q-px-md">
|
<q-toolbar class="q-py-sm q-px-md">
|
||||||
|
@ -41,18 +57,3 @@
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
</q-header>
|
</q-header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useState } from 'src/composables/useState';
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
|
||||||
import UserPanel from 'src/components/UserPanel.vue';
|
|
||||||
|
|
||||||
const session = useSession();
|
|
||||||
const state = useState();
|
|
||||||
const user = state.getUser();
|
|
||||||
const token = session.getToken();
|
|
||||||
|
|
||||||
defineEmits<{
|
|
||||||
(event: 'toggle-drawer'): void;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,51 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, computed } from 'vue';
|
||||||
|
import { Dark, useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const state = useState();
|
||||||
|
const session = useSession();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
|
const darkMode = computed({
|
||||||
|
get() {
|
||||||
|
return Dark.isActive;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
Dark.set(value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = state.getUser();
|
||||||
|
const token = session.getToken();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const roles = await axios.get('/api/accounts/acl')
|
||||||
|
state.setUser(roles.user);
|
||||||
|
} catch (error) {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('errors.statusUnauthorized'),
|
||||||
|
type: 'negative',
|
||||||
|
});
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
session.destroy();
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-menu>
|
<q-menu>
|
||||||
<div class="row no-wrap q-pa-md">
|
<div class="row no-wrap q-pa-md">
|
||||||
|
@ -11,7 +59,12 @@
|
||||||
true-value="en"
|
true-value="en"
|
||||||
v-model="locale"
|
v-model="locale"
|
||||||
/>
|
/>
|
||||||
<q-toggle v-model="darkMode" checked-icon="dark_mode" color="orange" unchecked-icon="light_mode" />
|
<q-toggle
|
||||||
|
v-model="darkMode"
|
||||||
|
checked-icon="dark_mode"
|
||||||
|
color="orange"
|
||||||
|
unchecked-icon="light_mode"
|
||||||
|
/>
|
||||||
|
|
||||||
<q-btn color="orange" outline size="sm" label="Settings" icon="settings" />
|
<q-btn color="orange" outline size="sm" label="Settings" icon="settings" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,63 +82,18 @@
|
||||||
<div class="text-subtitle1 q-mt-md">
|
<div class="text-subtitle1 q-mt-md">
|
||||||
<strong>{{ user.nickname }}</strong>
|
<strong>{{ user.nickname }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.username }}</div>
|
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.name }}</div>
|
||||||
|
|
||||||
<q-btn color="orange" flat label="Log Out" size="sm" icon="logout" @click="logout()" v-close-popup />
|
<q-btn
|
||||||
|
color="orange"
|
||||||
|
flat
|
||||||
|
label="Log Out"
|
||||||
|
size="sm"
|
||||||
|
icon="logout"
|
||||||
|
@click="logout()"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, computed } from 'vue';
|
|
||||||
import { Dark, useQuasar } from 'quasar';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import { useState } from 'src/composables/useState';
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
|
||||||
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const state = useState();
|
|
||||||
const session = useSession();
|
|
||||||
const router = useRouter();
|
|
||||||
const { t, locale } = useI18n();
|
|
||||||
|
|
||||||
const darkMode = computed({
|
|
||||||
get(): boolean {
|
|
||||||
return Dark.isActive;
|
|
||||||
},
|
|
||||||
set(value: boolean): void {
|
|
||||||
Dark.set(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const user = state.getUser();
|
|
||||||
const token = session.getToken();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
axios
|
|
||||||
.get('/api/accounts/acl')
|
|
||||||
.then(({ data }) => {
|
|
||||||
state.setUser({
|
|
||||||
id: data.user.id,
|
|
||||||
username: data.user.name,
|
|
||||||
nickname: data.user.nickname,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
quasar.notify({
|
|
||||||
message: t('errors.statusUnauthorized'),
|
|
||||||
type: 'negative',
|
|
||||||
});
|
|
||||||
logout();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function logout(): void {
|
|
||||||
session.destroy();
|
|
||||||
router.push('/login');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,28 +1,46 @@
|
||||||
import { describe, expect, it } from '@jest/globals';
|
import { describe, expect, it, jest } from '@jest/globals';
|
||||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
import { createWrapper, axios, flushPromises } from 'app/test/jest/jestHelpers';
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import { i18n } from 'src/boot/i18n';
|
|
||||||
import UserPanel from '../UserPanel.vue';
|
import UserPanel from '../UserPanel.vue';
|
||||||
|
|
||||||
// Specify here Quasar config you'll need to test your component
|
const mockPush = jest.fn();
|
||||||
installQuasarPlugin();
|
|
||||||
const routerPushMock = jest.fn();
|
|
||||||
|
|
||||||
jest.mock('vue-router', () => ({
|
jest.mock('vue-router', () => ({
|
||||||
useRouter: () => ({
|
useRouter: () => ({
|
||||||
push: routerPushMock,
|
push: mockPush,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('UserPanel', () => {
|
describe('UserPanel', () => {
|
||||||
it('should have the function logout defined', () => {
|
describe('onMounted', () => {
|
||||||
const wrapper = mount(UserPanel, {
|
it('should define the user into state', async () => {
|
||||||
global: {
|
const userMock = {
|
||||||
plugins: [i18n]
|
user: {
|
||||||
|
id: 1,
|
||||||
|
name: 'myName',
|
||||||
|
nickname: 'myNickName'
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
const { vm } = wrapper;
|
jest.spyOn(axios, 'get').mockResolvedValue(userMock);
|
||||||
|
const { vm } = createWrapper(UserPanel);
|
||||||
|
|
||||||
expect(vm.logout).toBeDefined();
|
await flushPromises()
|
||||||
|
|
||||||
|
const expectedUser = expect.objectContaining(userMock.user)
|
||||||
|
expect(vm.state.getUser().value).toEqual(expectedUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should logout and notify the expected error', async () => {
|
||||||
|
jest.spyOn(axios, 'get').mockRejectedValue(new Error('error'));
|
||||||
|
|
||||||
|
const { vm } = createWrapper(UserPanel);
|
||||||
|
|
||||||
|
jest.spyOn(vm.quasar, 'notify');
|
||||||
|
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining(
|
||||||
|
{ 'type': 'negative' }
|
||||||
|
));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
export interface Todo {
|
|
||||||
id: number;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Meta {
|
|
||||||
totalCount: number;
|
|
||||||
}
|
|
|
@ -1,26 +1,14 @@
|
||||||
import { useState } from './useState';
|
import { useState } from './useState';
|
||||||
|
|
||||||
interface Session {
|
export function useSession() {
|
||||||
token: string;
|
function getToken() {
|
||||||
keepLogin: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface useSession {
|
|
||||||
getToken: () => string;
|
|
||||||
setToken: (data: Session) => void;
|
|
||||||
destroy: () => void;
|
|
||||||
isLoggedIn: () => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSession(): useSession {
|
|
||||||
function getToken(): string {
|
|
||||||
const localToken = localStorage.getItem('token');
|
const localToken = localStorage.getItem('token');
|
||||||
const sessionToken = sessionStorage.getItem('token');
|
const sessionToken = sessionStorage.getItem('token');
|
||||||
|
|
||||||
return localToken || sessionToken || '';
|
return localToken || sessionToken || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setToken(data: Session): void {
|
function setToken(data) {
|
||||||
if (data.keepLogin) {
|
if (data.keepLogin) {
|
||||||
localStorage.setItem('token', data.token);
|
localStorage.setItem('token', data.token);
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +16,7 @@ export function useSession(): useSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroy(): void {
|
function destroy() {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
sessionStorage.getItem('token');
|
sessionStorage.getItem('token');
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
const user = ref({
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
nickname: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const roles = ref([]);
|
||||||
|
|
||||||
|
export function useState() {
|
||||||
|
function getUser() {
|
||||||
|
return computed(() => {
|
||||||
|
return {
|
||||||
|
id: user.value.id,
|
||||||
|
name: user.value.name,
|
||||||
|
nickname: user.value.nickname,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUser(data) {
|
||||||
|
user.value = {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
nickname: data.nickname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRoles() {
|
||||||
|
return computed(() => {
|
||||||
|
return roles.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRoles(data) {
|
||||||
|
roles.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getUser,
|
||||||
|
setUser,
|
||||||
|
getRoles,
|
||||||
|
setRoles,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,59 +0,0 @@
|
||||||
import { ref, Ref, computed, ComputedRef } from 'vue';
|
|
||||||
|
|
||||||
interface User {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
nickname: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface useState {
|
|
||||||
getUser: () => ComputedRef<User>;
|
|
||||||
setUser: (data: User) => void;
|
|
||||||
getRoles: () => ComputedRef<string[]>;
|
|
||||||
setRoles: (data: string[]) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const user: Ref<User> = ref({
|
|
||||||
id: 0,
|
|
||||||
username: '',
|
|
||||||
nickname: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const roles: Ref<string[]> = ref([]);
|
|
||||||
|
|
||||||
export function useState(): useState {
|
|
||||||
function getUser(): ComputedRef<User> {
|
|
||||||
return computed(() => {
|
|
||||||
return {
|
|
||||||
id: user.value.id,
|
|
||||||
username: user.value.username,
|
|
||||||
nickname: user.value.nickname,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUser(data: User): void {
|
|
||||||
user.value = {
|
|
||||||
id: data.id,
|
|
||||||
username: data.username,
|
|
||||||
nickname: data.nickname,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRoles(): ComputedRef<string[]> {
|
|
||||||
return computed(() => {
|
|
||||||
return roles.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRoles(data: string[]): void {
|
|
||||||
roles.value = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getUser,
|
|
||||||
setUser,
|
|
||||||
getRoles,
|
|
||||||
setRoles,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
declare namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
NODE_ENV: string;
|
|
||||||
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
|
|
||||||
VUE_ROUTER_BASE: string | undefined;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function toLowerCase(value) {
|
||||||
|
return value.toLowerCase();
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
export default function toLowerCase(value: string): string {
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
|
@ -18,7 +18,6 @@
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
|
||||||
<link rel="icon" type="image/ico" href="favicon.ico" />
|
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- DO NOT touch the following DIV -->
|
<!-- DO NOT touch the following DIV -->
|
||||||
<div id="q-app"></div>
|
<div id="q-app"></div>
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import Navbar from 'src/components/Navbar.vue';
|
||||||
|
const drawer = ref(false);
|
||||||
|
const miniState = ref(true);
|
||||||
|
|
||||||
|
function onToggleDrawer() {
|
||||||
|
drawer.value = !drawer.value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="hHh lpR fFf">
|
<q-layout view="hHh lpR fFf">
|
||||||
<Navbar @toggle-drawer="onToggleDrawer()" />
|
<Navbar @toggle-drawer="onToggleDrawer()" />
|
||||||
|
@ -13,13 +24,23 @@
|
||||||
>
|
>
|
||||||
<q-scroll-area class="fit text-grey-8">
|
<q-scroll-area class="fit text-grey-8">
|
||||||
<q-list padding>
|
<q-list padding>
|
||||||
<q-item clickable v-ripple :to="{ path: '/dashboard' }" active-class="text-orange">
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:to="{ path: '/dashboard' }"
|
||||||
|
active-class="text-orange"
|
||||||
|
>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-icon name="dashboard" />
|
<q-icon name="dashboard" />
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>Dashboard</q-item-section>
|
<q-item-section>Dashboard</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item clickable v-ripple :to="{ path: '/customer' }" active-class="text-orange">
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:to="{ path: '/customer' }"
|
||||||
|
active-class="text-orange"
|
||||||
|
>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-icon name="people" />
|
<q-icon name="people" />
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
@ -65,15 +86,4 @@
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import Navbar from 'src/components/Navbar.vue';
|
|
||||||
const drawer = ref(false);
|
|
||||||
const miniState = ref(true);
|
|
||||||
|
|
||||||
function onToggleDrawer(): void {
|
|
||||||
drawer.value = !drawer.value;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
|
@ -1,3 +1,32 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const customers = [
|
||||||
|
{
|
||||||
|
id: 1101,
|
||||||
|
name: 'Bruce Wayne',
|
||||||
|
username: 'batman',
|
||||||
|
email: 'batman@gotham',
|
||||||
|
phone: '555-555-5555',
|
||||||
|
expanded: ref(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1102,
|
||||||
|
name: 'James Gordon',
|
||||||
|
username: 'jamesgordon',
|
||||||
|
email: 'jamesgordon@gotham',
|
||||||
|
phone: '555-555-1111',
|
||||||
|
expanded: ref(false),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function navigate(id) {
|
||||||
|
router.push({ path: `/customer/${id}` });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<div class="column items-center q-gutter-y-md">
|
<div class="column items-center q-gutter-y-md">
|
||||||
|
@ -18,7 +47,7 @@
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>Phone</q-item-label>
|
<q-item-label caption>Phone</q-item-label>
|
||||||
<q-item-label>{{ customer.phone }} </q-item-label>
|
<q-item-label>{{ customer.phone }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
@ -38,7 +67,13 @@
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-separator vertical />
|
<q-separator vertical />
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(customer.id)" />
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="arrow_circle_right"
|
||||||
|
@click="navigate(customer.id)"
|
||||||
|
/>
|
||||||
<q-btn flat round color="accent" icon="preview" />
|
<q-btn flat round color="accent" icon="preview" />
|
||||||
<q-btn flat round color="accent" icon="vn:ticket" />
|
<q-btn flat round color="accent" icon="vn:ticket" />
|
||||||
<q-card-actions>
|
<q-card-actions>
|
||||||
|
@ -94,49 +129,12 @@
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card-section> -->
|
</q-card-section>-->
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, Ref } from 'vue';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
interface Customer {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
expanded: Ref<boolean>;
|
|
||||||
}
|
|
||||||
const customers: Customer[] = [
|
|
||||||
{
|
|
||||||
id: 1101,
|
|
||||||
name: 'Bruce Wayne',
|
|
||||||
username: 'batman',
|
|
||||||
email: 'batman@gotham',
|
|
||||||
phone: '555-555-5555',
|
|
||||||
expanded: ref(false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1102,
|
|
||||||
name: 'James Gordon',
|
|
||||||
username: 'jamesgordon',
|
|
||||||
email: 'jamesgordon@gotham',
|
|
||||||
phone: '555-555-1111',
|
|
||||||
expanded: ref(false),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function navigate(id: number) {
|
|
||||||
router.push({ path: `/customer/${id}` });
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.card {
|
.card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="ts" setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
const slide = ref('style');
|
const slide = ref('style');
|
||||||
const slideText = 'Description text';
|
const slideText = 'Description text';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<q-banner
|
<q-banner
|
||||||
|
|
|
@ -1,3 +1,67 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { Dark, useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const session = useSession();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
|
let username = ref('');
|
||||||
|
let password = ref('');
|
||||||
|
let keepLogin = ref(true);
|
||||||
|
|
||||||
|
const darkMode = computed({
|
||||||
|
get() {
|
||||||
|
return Dark.isActive;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
Dark.set(value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post('/api/accounts/login', {
|
||||||
|
user: username.value,
|
||||||
|
password: password.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
session.setToken({
|
||||||
|
token: data.token,
|
||||||
|
keepLogin: keepLogin.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
quasar.notify({
|
||||||
|
message: t('login.loginSuccess'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
|
||||||
|
await router.push({ path: '/dashboard' });
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
const errorCode = error.response && error.response.status;
|
||||||
|
if (errorCode === 401) {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('login.loginError'),
|
||||||
|
type: 'negative',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('errors.statusInternalServerError'),
|
||||||
|
type: 'negative',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-layout>
|
<q-layout>
|
||||||
<q-header class="transparent">
|
<q-header class="transparent">
|
||||||
|
@ -69,70 +133,6 @@
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, ref } from 'vue';
|
|
||||||
import { Dark, useQuasar } from 'quasar';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
|
||||||
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const session = useSession();
|
|
||||||
const router = useRouter();
|
|
||||||
const { t, locale } = useI18n();
|
|
||||||
|
|
||||||
let username = ref('');
|
|
||||||
let password = ref('');
|
|
||||||
let keepLogin = ref(true);
|
|
||||||
|
|
||||||
const darkMode = computed({
|
|
||||||
get(): boolean {
|
|
||||||
return Dark.isActive;
|
|
||||||
},
|
|
||||||
set(value: boolean): void {
|
|
||||||
Dark.set(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
async function onSubmit(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const { data } = await axios.post('/api/accounts/login', {
|
|
||||||
user: username.value,
|
|
||||||
password: password.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
session.setToken({
|
|
||||||
token: data.token,
|
|
||||||
keepLogin: keepLogin.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
quasar.notify({
|
|
||||||
message: t('login.loginSuccess'),
|
|
||||||
type: 'positive',
|
|
||||||
});
|
|
||||||
|
|
||||||
await router.push({ path: '/dashboard' });
|
|
||||||
} catch (error) {
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
const errorCode = error.response && error.response.status;
|
|
||||||
if (errorCode === 401) {
|
|
||||||
quasar.notify({
|
|
||||||
message: t('login.loginError'),
|
|
||||||
type: 'negative',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quasar.notify({
|
|
||||||
message: t('errors.statusInternalServerError'),
|
|
||||||
type: 'negative',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#login {
|
#login {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,35 +1,22 @@
|
||||||
import { jest, describe, expect, it } from '@jest/globals';
|
import { jest, describe, expect, it, beforeAll } from '@jest/globals';
|
||||||
import { mount } from '@vue/test-utils';
|
import { createWrapper, axios } from 'app/test/jest/jestHelpers';
|
||||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
|
||||||
import { i18n } from 'src/boot/i18n';
|
|
||||||
import { Notify } from 'quasar';
|
|
||||||
import axios from 'axios';
|
|
||||||
import Login from '../Login.vue';
|
import Login from '../Login.vue';
|
||||||
|
|
||||||
// Specify here Quasar config you'll need to test your component
|
const mockPush = jest.fn();
|
||||||
installQuasarPlugin({
|
|
||||||
plugins: {
|
|
||||||
Notify
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const routerPushMock = jest.fn();
|
|
||||||
|
|
||||||
jest.mock('vue-router', () => ({
|
jest.mock('vue-router', () => ({
|
||||||
useRouter: () => ({
|
useRouter: () => ({
|
||||||
push: routerPushMock,
|
push: mockPush,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
describe('Login', () => {
|
describe('Login', () => {
|
||||||
it('should successfully set the token into session', async () => {
|
let vm;
|
||||||
const wrapper = mount(Login, {
|
beforeAll(() => {
|
||||||
global: {
|
vm = createWrapper(Login).vm;
|
||||||
plugins: [i18n]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const { vm } = wrapper;
|
|
||||||
|
|
||||||
|
it('should successfully set the token into session', async () => {
|
||||||
jest.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
jest.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
||||||
jest.spyOn(vm.quasar, 'notify')
|
jest.spyOn(vm.quasar, 'notify')
|
||||||
|
|
||||||
|
@ -45,13 +32,6 @@ describe('Login', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set the token into session if any error occurred', async () => {
|
it('should not set the token into session if any error occurred', async () => {
|
||||||
const wrapper = mount(Login, {
|
|
||||||
global: {
|
|
||||||
plugins: [i18n]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const { vm } = wrapper;
|
|
||||||
|
|
||||||
jest.spyOn(axios, 'post').mockRejectedValue(new Error('error'));
|
jest.spyOn(axios, 'post').mockRejectedValue(new Error('error'));
|
||||||
jest.spyOn(vm.quasar, 'notify')
|
jest.spyOn(vm.quasar, 'notify')
|
||||||
|
|
|
@ -5,15 +5,16 @@
|
||||||
|
|
||||||
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div>
|
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div>
|
||||||
|
|
||||||
<q-btn class="q-mt-xl" color="white" text-color="blue" unelevated to="/" label="Go Home" no-caps />
|
<q-btn
|
||||||
|
class="q-mt-xl"
|
||||||
|
color="white"
|
||||||
|
text-color="blue"
|
||||||
|
unelevated
|
||||||
|
to="/"
|
||||||
|
label="Go Home"
|
||||||
|
no-caps
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'Error404',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Forces TS to apply `@quasar/app` augmentations of `quasar` package
|
|
||||||
// Removing this would break `quasar/wrappers` imports as those typings are declared
|
|
||||||
// into `@quasar/app`
|
|
||||||
// As a side effect, since `@quasar/app` reference `quasar` to augment it,
|
|
||||||
// this declaration also apply `quasar` own
|
|
||||||
// augmentations (eg. adds `$q` into Vue component context)
|
|
||||||
/// <reference types="@quasar/app" />
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { route } from 'quasar/wrappers';
|
||||||
|
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router';
|
||||||
|
import routes from './routes';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If not building with SSR mode, you can
|
||||||
|
* directly export the Router instantiation;
|
||||||
|
*
|
||||||
|
* The function below can be async too; either use
|
||||||
|
* async/await or return a Promise which resolves
|
||||||
|
* with the Router instance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default route(function (/* { store, ssrContext } */) {
|
||||||
|
const createHistory = process.env.SERVER
|
||||||
|
? createMemoryHistory
|
||||||
|
: process.env.VUE_ROUTER_MODE === 'history'
|
||||||
|
? createWebHistory
|
||||||
|
: createWebHashHistory;
|
||||||
|
|
||||||
|
const Router = createRouter({
|
||||||
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
|
routes,
|
||||||
|
|
||||||
|
// Leave this as is and make changes in quasar.conf.js instead!
|
||||||
|
// quasar.conf.js -> build -> vueRouterMode
|
||||||
|
// quasar.conf.js -> build -> publicPath
|
||||||
|
history: createHistory(process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Router;
|
||||||
|
});
|
|
@ -1,71 +0,0 @@
|
||||||
import { route } from 'quasar/wrappers';
|
|
||||||
import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
|
||||||
import routes from './routes';
|
|
||||||
import { i18n } from 'src/boot/i18n';
|
|
||||||
import { useSession } from 'src/composables/useSession';
|
|
||||||
const session = useSession();
|
|
||||||
/*
|
|
||||||
* If not building with SSR mode, you can
|
|
||||||
* directly export the Router instantiation;
|
|
||||||
*
|
|
||||||
* The function below can be async too; either use
|
|
||||||
* async/await or return a Promise which resolves
|
|
||||||
* with the Router instance.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default route(function (/* { store, ssrContext } */) {
|
|
||||||
const createHistory = process.env.SERVER
|
|
||||||
? createMemoryHistory
|
|
||||||
: process.env.VUE_ROUTER_MODE === 'history'
|
|
||||||
? createWebHistory
|
|
||||||
: createWebHashHistory;
|
|
||||||
|
|
||||||
const Router = createRouter({
|
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
|
||||||
routes,
|
|
||||||
|
|
||||||
// Leave this as is and make changes in quasar.conf.js instead!
|
|
||||||
// quasar.conf.js -> build -> vueRouterMode
|
|
||||||
// quasar.conf.js -> build -> publicPath
|
|
||||||
history: createHistory(process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE),
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.beforeEach((to, from, next) => {
|
|
||||||
const { isLoggedIn } = session;
|
|
||||||
|
|
||||||
if (!isLoggedIn && to.name !== 'Login') {
|
|
||||||
next({ path: '/login', query: { redirect: to.fullPath } });
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.afterEach((to) => {
|
|
||||||
interface Meta {
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = i18n.global;
|
|
||||||
let title = '';
|
|
||||||
|
|
||||||
const parent = to.matched[1];
|
|
||||||
if (parent) {
|
|
||||||
const parentMeta: Meta = parent.meta;
|
|
||||||
if (parentMeta && parentMeta.title) {
|
|
||||||
title += t(`pages.${parentMeta.title}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//const childTitle: string = childMeta.title;
|
|
||||||
|
|
||||||
const childMeta: Meta = to.meta;
|
|
||||||
if (childMeta && childMeta.title) {
|
|
||||||
if (title != '') title += ' - ';
|
|
||||||
|
|
||||||
title += t(`pages.${childMeta.title}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = title;
|
|
||||||
});
|
|
||||||
|
|
||||||
return Router;
|
|
||||||
});
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
const routes = [
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
|
@ -10,7 +8,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Main',
|
name: 'Main',
|
||||||
component: () => import('../layouts/Main.vue'),
|
component: () => import('../layouts/MainLayout.vue'),
|
||||||
redirect: { name: 'Dashboard' },
|
redirect: { name: 'Dashboard' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -23,7 +21,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
path: '/customer',
|
path: '/customer',
|
||||||
name: 'Customer',
|
name: 'Customer',
|
||||||
meta: { title: 'customers' },
|
meta: { title: 'customers' },
|
||||||
component: () => import('../pages/Customer/Customer.vue'),
|
component: () => import('../pages/Customer/CustomerLayout.vue'),
|
||||||
redirect: { name: 'List' },
|
redirect: { name: 'List' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -35,7 +33,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
name: 'Card',
|
name: 'Card',
|
||||||
component: () => import('../pages/Customer/Card/Card.vue'),
|
component: () => import('../pages/Customer/Card/CustomerCard.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
|
@ -1,7 +0,0 @@
|
||||||
// Mocks all files ending in `.vue` showing them as plain Vue instances
|
|
||||||
/* eslint-disable */
|
|
||||||
declare module '*.vue' {
|
|
||||||
import type { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
/// <reference types="cypress" />
|
|
||||||
// Use `cy.dataCy` custom command for more robust tests
|
|
||||||
// See https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements
|
|
||||||
|
|
||||||
// ** This file is an example of how to write Cypress tests, you can safely delete it **
|
|
||||||
|
|
||||||
// This test will pass when run against a clean Quasar project
|
|
||||||
describe('Landing', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.visit('/#/login');
|
|
||||||
});
|
|
||||||
it('should log in', () => {
|
|
||||||
cy.get('input[aria-label="Username"]').type('developer')
|
|
||||||
cy.get('input[aria-label="Password"]').type('nightmare')
|
|
||||||
cy.get('button[type="submit"]').click()
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
describe('Login', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/#/login');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log in', () => {
|
||||||
|
cy.get('input[aria-label="Username"]').type('developer')
|
||||||
|
cy.get('input[aria-label="Password"]').type('nightmare')
|
||||||
|
cy.get('button[type="submit"]').click()
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,14 +15,14 @@
|
||||||
|
|
||||||
// cypress/plugins/index.js
|
// cypress/plugins/index.js
|
||||||
|
|
||||||
const { injectDevServer } = require('@quasar/quasar-app-extension-testing-e2e-cypress/cct-dev-server');
|
// const {injectDevServer} = require('@quasar/quasar-app-extension-testing-e2e-cypress/cct-dev-server');
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @type {Cypress.PluginConfig}
|
// * @type {Cypress.PluginConfig}
|
||||||
*/
|
// */
|
||||||
module.exports = async (on, config) => {
|
module.exports = async (on, config) => {
|
||||||
// Enable component testing, you can safely remove this
|
// // Enable component testing, you can safely remove this
|
||||||
// if you don't plan to use Cypress for unit tests
|
// // if you don't plan to use Cypress for unit tests
|
||||||
// if (config.testingType === 'component') {
|
// if (config.testingType === 'component') {
|
||||||
// await injectDevServer(on, config);
|
// await injectDevServer(on, config);
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
import { describe, expect, it } from '@jest/globals';
|
|
||||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
|
||||||
import { mount, shallowMount } from '@vue/test-utils';
|
|
||||||
import { QBtn } from 'quasar';
|
|
||||||
import MyButton from './demo/MyButton.vue';
|
|
||||||
|
|
||||||
// Specify here Quasar config you'll need to test your component
|
|
||||||
installQuasarPlugin();
|
|
||||||
|
|
||||||
describe('MyButton', () => {
|
|
||||||
it('has increment method', () => {
|
|
||||||
const wrapper = mount(MyButton);
|
|
||||||
const { vm } = wrapper;
|
|
||||||
|
|
||||||
expect(typeof vm.increment).toBe('function');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can check the inner text content', () => {
|
|
||||||
const wrapper = mount(MyButton);
|
|
||||||
const { vm } = wrapper;
|
|
||||||
|
|
||||||
expect(vm.$el.textContent).toContain('rocket muffin');
|
|
||||||
expect(wrapper.find('.content').text()).toContain('rocket muffin');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the correct default data', () => {
|
|
||||||
const wrapper = mount(MyButton);
|
|
||||||
const { vm } = wrapper;
|
|
||||||
|
|
||||||
expect(typeof vm.counter).toBe('number');
|
|
||||||
expect(vm.counter).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('correctly updates counter when button is pressed', async () => {
|
|
||||||
const wrapper = shallowMount(MyButton);
|
|
||||||
const { vm } = wrapper;
|
|
||||||
const button = wrapper.findComponent(QBtn);
|
|
||||||
await button.trigger('click');
|
|
||||||
expect(vm.counter).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
|
||||||
import { describe, expect, it } from '@jest/globals';
|
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import MyDialog from './demo/MyDialog.vue';
|
|
||||||
|
|
||||||
installQuasarPlugin();
|
|
||||||
|
|
||||||
describe('MyDialog', () => {
|
|
||||||
it('should mount MyDialog', () => {
|
|
||||||
const wrapper = mount(MyDialog, {
|
|
||||||
data: () => ({
|
|
||||||
isDialogOpen: true,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.exists()).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,23 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
const props = defineProps({
|
|
||||||
incrementStep: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const counter = ref(0);
|
|
||||||
const input = ref('rocket muffin');
|
|
||||||
function increment() {
|
|
||||||
counter.value += props.incrementStep;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<p class="content">{{ input }}</p>
|
|
||||||
<span>{{ counter }}</span>
|
|
||||||
<q-btn class="button" @click="increment()"></q-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,12 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
const isDialogOpen = ref(false);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<q-dialog v-model="isDialogOpen">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>Custom dialog which should be tested</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</template>
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { mount, flushPromises } from '@vue/test-utils';
|
||||||
|
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
||||||
|
import { i18n } from 'src/boot/i18n';
|
||||||
|
import { Notify } from 'quasar';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
// Specify here Quasar config you'll need to test your component
|
||||||
|
installQuasarPlugin({
|
||||||
|
plugins: {
|
||||||
|
Notify
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function createWrapper(component) {
|
||||||
|
const wrapper = mount(component, {
|
||||||
|
global: {
|
||||||
|
plugins: [i18n]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const vm = wrapper.vm;
|
||||||
|
|
||||||
|
return { vm, wrapper };
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
axios,
|
||||||
|
flushPromises
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "@quasar/app/tsconfig-preset",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "."
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue