From ac4463c229630b3f48c69eaf409bdb4c6aec1da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Odini?= Date: Sat, 25 May 2024 18:39:54 +0200 Subject: [PATCH] test: setup linting with ESLint & fix files (#586) --- .github/labeler.yml | 6 +- .github/workflows/lint-and-test.yml | 25 + .github/workflows/tests.yml | 16 - CONTRIBUTING.md | 6 + cypress.config.js | 6 +- eslint.config.js | 21 + package.json | 4 + postcss.config.js | 5 - src/components/BarcodeManualInputDialog.vue | 11 +- src/components/BarcodeScannerDialog.vue | 15 +- src/components/ChangeCurrencyDialog.vue | 10 +- src/components/FilterMenu.vue | 15 +- src/components/Footer.vue | 28 +- src/components/Header.vue | 40 +- src/components/LocationCard.vue | 7 +- src/components/LocationSelectorDialog.vue | 63 ++- src/components/OpenFoodFactsAddMenu.vue | 20 +- src/components/OrderMenu.vue | 9 +- src/components/PriceActionMenuButton.vue | 35 +- src/components/PriceAddButton.vue | 3 +- src/components/PriceCard.vue | 96 ++-- src/components/PriceCountChip.vue | 12 +- .../PriceDeleteConfirmationDialog.vue | 27 +- src/components/PriceEditDialog.vue | 37 +- src/components/PriceFooterRow.vue | 40 +- src/components/PriceInputRow.vue | 46 +- src/components/PriceLabels.vue | 7 +- src/components/PriceLocationChip.vue | 12 +- src/components/PriceOrigins.vue | 6 +- src/components/PriceOwnerChip.vue | 12 +- src/components/PricePriceRow.vue | 22 +- src/components/PriceProof.vue | 14 +- src/components/ProductBarcodeChip.vue | 9 +- src/components/ProductBrands.vue | 17 +- src/components/ProductBrandsDialog.vue | 10 +- src/components/ProductCard.vue | 72 +-- src/components/ProductCategoriesChip.vue | 14 +- src/components/ProductCategoriesDialog.vue | 10 +- src/components/ProductLabelsChip.vue | 13 +- src/components/ProductLabelsDialog.vue | 10 +- src/components/ProductQuantityChip.vue | 22 +- src/components/ProofActionMenuButton.vue | 35 +- src/components/ProofCard.vue | 20 +- .../ProofDeleteConfirmationDialog.vue | 27 +- src/components/ProofEditDialog.vue | 19 +- src/components/ProofFooter.vue | 25 +- src/components/ProofPrivateChip.vue | 7 +- src/components/ProofTypeChip.vue | 9 +- src/components/RelativeDateTimeChip.vue | 11 +- src/components/ShareButton.vue | 15 +- src/components/UserCard.vue | 7 +- src/components/UserRecentProofsDialog.vue | 12 +- src/i18n/localeManager.js | 2 - src/store.js | 3 - src/utils.js | 2 +- src/views/AddPriceHome.vue | 30 +- src/views/AddPriceMultiple.vue | 216 ++++---- src/views/AddPriceSingle.vue | 170 +++--- src/views/BrandDetail.vue | 53 +- src/views/CategoryDetail.vue | 53 +- src/views/Home.vue | 30 +- src/views/LocationDetail.vue | 64 +-- src/views/LocationList.vue | 12 +- src/views/PriceList.vue | 12 +- src/views/ProductDetail.vue | 84 +-- src/views/ProductList.vue | 34 +- src/views/ProofDetail.vue | 30 +- src/views/Search.vue | 39 +- src/views/SignIn.vue | 17 +- src/views/Stats.vue | 10 +- src/views/User.vue | 9 - src/views/UserDashboard.vue | 8 +- src/views/UserDashboardPriceList.vue | 18 +- src/views/UserDashboardProofList.vue | 21 +- src/views/UserDetail.vue | 52 +- src/views/UserList.vue | 12 +- src/views/UserSettings.vue | 68 ++- yarn.lock | 493 +++++++++++++++++- 78 files changed, 1725 insertions(+), 857 deletions(-) create mode 100644 .github/workflows/lint-and-test.yml delete mode 100644 .github/workflows/tests.yml create mode 100644 eslint.config.js delete mode 100644 postcss.config.js delete mode 100644 src/views/User.vue diff --git a/.github/labeler.yml b/.github/labeler.yml index eaf6a94fca3..e6c79fcbf69 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -5,8 +5,7 @@ GitHub Actions: Price addition: - 'src/views/AddPriceHome.vue' - -Multiple price addition: +- 'src/views/AddPriceSingle.vue' - 'src/views/AddPriceMultiple.vue' Brands: @@ -21,13 +20,11 @@ Brands: 🔍 Search: - 'src/views/Search.vue' -- 'src/views/SignIn.vue' Stats: - 'src/views/Stats.vue' User: -- 'src/views/User.vue' - 'src/views/UserList.vue' - 'src/views/UserDetail.vue' @@ -36,4 +33,3 @@ User dashboard: ⚙️ Settings: - 'src/views/UserSettings.vue' - diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml new file mode 100644 index 00000000000..8786f9ebab1 --- /dev/null +++ b/.github/workflows/lint-and-test.yml @@ -0,0 +1,25 @@ +name: Linting & e2e tests + +on: push + +jobs: + lint-and-test: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + # Install npm dependencies, cache them correctly + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + # just perform install + runTests: false + - name: Lint with ESLint + run: yarn lint + - name: Run e2e tests + uses: cypress-io/github-action@v6 + with: + # we have already installed all dependencies above + install: false + # run server in the background + start: yarn dev diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 7e842dbf7ff..00000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: End-to-end tests - -on: push - -jobs: - cypress-run: - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - # Install npm dependencies, cache them correctly - # and run all Cypress tests - - name: Cypress run - uses: cypress-io/github-action@v6 - with: - start: yarn dev diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78657dad9ab..a8a1137e834 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,12 @@ We use the [yarn](https://yarnpkg.com/getting-started/install) for package manag yarn build ``` +## Lint + +```sh +yarn lint +``` + ## Tests ```sh diff --git a/cypress.config.js b/cypress.config.js index 99389819234..c486708325f 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,6 +1,6 @@ -const { defineConfig } = require("cypress"); +import { defineConfig } from 'cypress' -module.exports = defineConfig({ +export default defineConfig({ e2e: { specPattern: '**/*.cy.js', fixturesFolder: 'tests/fixtures', @@ -13,4 +13,4 @@ module.exports = defineConfig({ }, baseUrl: 'http://localhost:5173', }, -}); +}) diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000000..55576e4373f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,21 @@ +import js from '@eslint/js' +import pluginVue from 'eslint-plugin-vue' + +export default [ + js.configs.recommended, + ...pluginVue.configs['flat/recommended'], + { + ignores: ['dist/**', '*.config.js', 'tests/**'], + }, + { + rules: { + 'no-unused-vars': 'warn', + 'vue/no-mutating-props': ['error', { 'shallowOnly': true }], + 'vue/no-reserved-component-names': 'off', + 'vue/multi-word-component-names': 'off', + 'vue/max-attributes-per-line': 'off', + 'vue/attribute-hyphenation': 'off', + 'vue/v-on-event-hyphenation': 'off', + } + }, +] diff --git a/package.json b/package.json index ffd9d97a957..dcb732d8aa1 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,14 @@ "name": "open-prices", "version": "0.0.0", "private": true, + "type": "module", "scripts": { "dev": "vite", "build": "vite build", "build-staging": "vite build --base=/app/ --mode preprod", "build-prod": "vite build --base=/app/ --mode prod", "preview": "vite preview", + "lint": "eslint", "cy:open": "cypress open", "cy:run": "cypress run", "test": "start-server-and-test dev http://localhost:5173 cy:run" @@ -37,6 +39,8 @@ "autoprefixer": "^10.4.16", "cross-env": "^7.0.3", "cypress": "^13.8.1", + "eslint": "^9.3.0", + "eslint-plugin-vue": "^9.26.0", "postcss": "^8.4.31", "start-server-and-test": "^2.0.3", "vite": "^4.5.0" diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 90d9fffcb1f..00000000000 --- a/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - }, -} diff --git a/src/components/BarcodeManualInputDialog.vue b/src/components/BarcodeManualInputDialog.vue index 243db3c575e..203fc6ef2e2 100644 --- a/src/components/BarcodeManualInputDialog.vue +++ b/src/components/BarcodeManualInputDialog.vue @@ -2,10 +2,10 @@ - {{ $t('BarcodeManualInput.Title') }} + {{ $t('BarcodeManualInput.Title') }} - + @@ -18,13 +18,15 @@ prepend-inner-icon="mdi-barcode" :hint="barcodeForm.barcode.length.toString()" persistent-hint - > + /> {{ $t('BarcodeManualInput.Submit') }} + > + {{ $t('BarcodeManualInput.Submit') }} + @@ -33,6 +35,7 @@ diff --git a/src/components/PriceLabels.vue b/src/components/PriceLabels.vue index a67da31ace1..6f6126ce481 100644 --- a/src/components/PriceLabels.vue +++ b/src/components/PriceLabels.vue @@ -2,7 +2,7 @@ {{ label.name }} - + @@ -12,7 +12,10 @@ import LabelTags from '../data/labels-tags.json' export default { props: { - priceLabels: Array + priceLabels: { + type: Array, + default: () => [] + } }, computed: { priceLabelsTagsList() { diff --git a/src/components/PriceLocationChip.vue b/src/components/PriceLocationChip.vue index 0d7da2f0da8..aa2c5de624a 100644 --- a/src/components/PriceLocationChip.vue +++ b/src/components/PriceLocationChip.vue @@ -1,6 +1,6 @@ @@ -26,10 +28,13 @@ import { defineAsyncComponent } from 'vue' export default { components: { - 'ProductBrandsDialog': defineAsyncComponent(() => import('../components/ProductBrandsDialog.vue')), + ProductBrandsDialog: defineAsyncComponent(() => import('../components/ProductBrandsDialog.vue')), }, props: { - productBrands: String, + productBrands: { + type: String, + default: null + }, readonly: { type: Boolean, default: false diff --git a/src/components/ProductBrandsDialog.vue b/src/components/ProductBrandsDialog.vue index e7233e73119..45d3e839307 100644 --- a/src/components/ProductBrandsDialog.vue +++ b/src/components/ProductBrandsDialog.vue @@ -2,10 +2,10 @@ - {{ $t('ProductCard.Brands') }} + {{ $t('ProductCard.Brands') }} - + @@ -19,8 +19,12 @@ diff --git a/src/components/ProofTypeChip.vue b/src/components/ProofTypeChip.vue index 3aa0d55c818..d42eddcd17f 100644 --- a/src/components/ProofTypeChip.vue +++ b/src/components/ProofTypeChip.vue @@ -1,10 +1,10 @@ @@ -34,7 +36,10 @@ import constants from '../constants' export default { props: { - 'overrideUrl': null, + overrideUrl: { + type: String, + default: null + }, }, data() { return { diff --git a/src/components/UserCard.vue b/src/components/UserCard.vue index d7e53d56f27..1dcd2b4a2d0 100644 --- a/src/components/UserCard.vue +++ b/src/components/UserCard.vue @@ -2,9 +2,10 @@ + @click="goToUser(user)" + > - + @@ -14,7 +15,7 @@ import { defineAsyncComponent } from 'vue' export default { components: { - 'PriceCountChip': defineAsyncComponent(() => import('../components/PriceCountChip.vue')), + PriceCountChip: defineAsyncComponent(() => import('../components/PriceCountChip.vue')), }, props: { user: { diff --git a/src/components/UserRecentProofsDialog.vue b/src/components/UserRecentProofsDialog.vue index f63c2381edf..da66841e7a8 100644 --- a/src/components/UserRecentProofsDialog.vue +++ b/src/components/UserRecentProofsDialog.vue @@ -2,15 +2,15 @@ - {{ $t('UserRecentProofsDialog.SelectRecentProof') }} + {{ $t('UserRecentProofsDialog.SelectRecentProof') }} - + - - + + @@ -26,8 +26,9 @@ import api from '../services/api' export default { components: { - 'ProofCard': defineAsyncComponent(() => import('../components/ProofCard.vue')), + ProofCard: defineAsyncComponent(() => import('../components/ProofCard.vue')), }, + emits: ['recentProofSelected', 'close'], data() { return { userProofList: [], @@ -37,7 +38,6 @@ export default { selectedProof: null, } }, - emits: ['recentProofSelected', 'close'], computed: { ...mapStores(useAppStore), username() { diff --git a/src/i18n/localeManager.js b/src/i18n/localeManager.js index 32e745d72b9..578a9f1c470 100644 --- a/src/i18n/localeManager.js +++ b/src/i18n/localeManager.js @@ -156,8 +156,6 @@ const localeManager = { const enKeys = Object.keys(enFlat) const localeKeys = Object.keys(localeFlat) - const enValues = Object.values(enFlat) - const localeValues = Object.values(localeFlat) let identicalValues = 0 let missingKeys = 0 for (let i = 0; i < enKeys.length; i++) { diff --git a/src/store.js b/src/store.js index b830eecfaf8..a1823992bbc 100644 --- a/src/store.js +++ b/src/store.js @@ -51,9 +51,6 @@ export const useAppStore = defineStore('app', { this.user.username = null this.user.token = null }, - setLastCurrencyUsed(currency) { - this.user.last_currency_used = currency - }, addRecentLocation(location) { this.user.recent_locations = utils.addObjectToArray(this.user.recent_locations, location, true) }, diff --git a/src/utils.js b/src/utils.js index ff7a6c7f9da..71edcb149e6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -224,7 +224,7 @@ function getMapCenter(results, source='nominatim') { if (source === 'photon') { return [results[0].geometry.coordinates[1], results[0].geometry.coordinates[0]] } - return [results[0].lat, results[0][lon]] + return [results[0].lat, results[0].lon] } diff --git a/src/views/AddPriceHome.vue b/src/views/AddPriceHome.vue index bed6b47af20..9defe5fde91 100644 --- a/src/views/AddPriceHome.vue +++ b/src/views/AddPriceHome.vue @@ -9,14 +9,14 @@ :title="$t('AddPriceHome.SingleProductMode.Title')" :subtitle="$t('AddPriceHome.SingleProductMode.Subtitle')" prepend-icon="mdi-barcode" - to="/add/single"> - + to="/add/single" + /> -
- -
+
+ +
@@ -24,16 +24,16 @@ :title="$t('AddPriceHome.MultipleProductMode.Title')" :subtitle="$t('AddPriceHome.MultipleProductMode.Subtitle')" prepend-icon="mdi-library-shelves" - to="/add/multiple/price-tag"> -
+ to="/add/multiple/price-tag" + /> - + to="/add/multiple/receipt" + /> @@ -41,17 +41,23 @@ v-model="signinSuccessMessage" color="success" :timeout="2000" - >{{ $t('AddPriceHome.SignedIn') }} + > + {{ $t('AddPriceHome.SignedIn') }} + {{ $t('AddPriceHome.PriceCreated') }} + > + {{ $t('AddPriceHome.PriceCreated') }} + {{ $t('AddPriceHome.Thanks') }} + > + {{ $t('AddPriceHome.Thanks') }} + diff --git a/src/views/AddPriceSingle.vue b/src/views/AddPriceSingle.vue index cfa5817f92c..48c84bd7f6f 100644 --- a/src/views/AddPriceSingle.vue +++ b/src/views/AddPriceSingle.vue @@ -5,24 +5,24 @@ - - @@ -52,11 +57,11 @@ import api from '../services/api' export default { components: { - 'FilterMenu': defineAsyncComponent(() => import('../components/FilterMenu.vue')), - 'OrderMenu': defineAsyncComponent(() => import('../components/OrderMenu.vue')), - 'ProductCard': defineAsyncComponent(() => import('../components/ProductCard.vue')), - 'OpenFoodFactsLink': defineAsyncComponent(() => import('../components/OpenFoodFactsLink.vue')), - 'ShareButton': defineAsyncComponent(() => import('../components/ShareButton.vue')) + FilterMenu: defineAsyncComponent(() => import('../components/FilterMenu.vue')), + OrderMenu: defineAsyncComponent(() => import('../components/OrderMenu.vue')), + ProductCard: defineAsyncComponent(() => import('../components/ProductCard.vue')), + OpenFoodFactsLink: defineAsyncComponent(() => import('../components/OpenFoodFactsLink.vue')), + ShareButton: defineAsyncComponent(() => import('../components/ShareButton.vue')) }, data() { return { @@ -80,6 +85,13 @@ export default { return defaultParams }, }, + watch: { + $route (newCategory, oldCategory) { + if (oldCategory && newCategory && newCategory.name == 'category-detail' && oldCategory.fullPath != newCategory.fullPath) { + this.initCategory() + } + } + }, mounted() { this.currentFilter = this.$route.query[constants.FILTER_PARAM] || this.currentFilter this.currentOrder = this.$route.query[constants.ORDER_PARAM] || this.currentOrder @@ -115,13 +127,6 @@ export default { // this.initCategory() will be called in watch $route } } - }, - watch: { - $route (newCategory, oldCategory) { - if (oldCategory && newCategory && newCategory.name == 'category-detail' && oldCategory.fullPath != newCategory.fullPath) { - this.initCategory() - } - } } } diff --git a/src/views/Home.vue b/src/views/Home.vue index 40548be9c48..36f432f6d01 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,12 +1,14 @@