Просмотр исходного кода

chore(lint): enforce JavaScript checks consistently

Co-authored-by: Cursor <cursoragent@cursor.com>
jamesread 16 часов назад
Родитель
Сommit
4c6b6ac0fe
28 измененных файлов с 136 добавлено и 84 удалено
  1. 7 6
      frontend/Makefile
  2. 2 0
      frontend/package.json
  3. 12 12
      frontend/vite.config.mjs
  4. 0 8
      integration-tests/.eslintrc.yml
  5. 9 3
      integration-tests/Makefile
  6. 31 0
      integration-tests/eslint.config.mjs
  7. 1 1
      integration-tests/lib/elements.js
  8. 36 0
      integration-tests/package-lock.json
  9. 5 1
      integration-tests/package.json
  10. 1 1
      integration-tests/scripts/find-flakey-tests-inf.mjs
  11. 1 1
      integration-tests/tests/argumentActionFlash/argumentActionFlash.mjs
  12. 1 1
      integration-tests/tests/argumentIdCollision/argumentIdCollision.mjs
  13. 1 2
      integration-tests/tests/authRequireGuestsToLogin/authRequireGuestsToLogin.mjs
  14. 1 1
      integration-tests/tests/checkbox/checkbox.mjs
  15. 1 1
      integration-tests/tests/checklist/checklist.mjs
  16. 1 1
      integration-tests/tests/confirmation/confirmation.mjs
  17. 1 1
      integration-tests/tests/dashboardsWithBasicFieldsets/dashboardsWithBasicFieldsets.mjs
  18. 0 1
      integration-tests/tests/emptyDashboardsAreHidden/emptyDashboardsAreHidden.mjs
  19. 1 3
      integration-tests/tests/hiddenFooter/hiddenFooter.mjs
  20. 2 5
      integration-tests/tests/hiddenNav/hiddenNav.mjs
  21. 1 1
      integration-tests/tests/justification/justification.mjs
  22. 5 6
      integration-tests/tests/localAuth/localAuth.mjs
  23. 1 2
      integration-tests/tests/logPersistence/logPersistence.mjs
  24. 1 1
      integration-tests/tests/multipleDropdowns/multipleDropdowns.mjs
  25. 8 15
      integration-tests/tests/oauthLoginGithub/githubOAuth.mjs
  26. 1 2
      integration-tests/tests/onlyDashboards/onlyDashboards.mjs
  27. 1 3
      integration-tests/tests/sleep/sleep.mjs
  28. 4 5
      integration-tests/tests/stdoutMostRecentExecution/stdoutMostRecentExecution.mjs

+ 7 - 6
frontend/Makefile

@@ -2,10 +2,11 @@ define delete-files
 	python3 -c "import shutil;shutil.rmtree('$(1)', ignore_errors=True)"
 endef
 
-codestyle:
-	npm install
-	npx eslint --fix main.js js/* resources/vue
-	npx stylelint style.css
+codestyle: deps
+	npm run lint
+
+codestyle-fix: deps
+	npm run lint:fix
 
 unittests: deps
 	npm test
@@ -14,11 +15,11 @@ clean:
 	$(call delete-files,dist)
 
 deps:
-	npm install
+	npm ci
 
 build:
 	npx vite build
 
 dist: deps clean build
 
-.PHONY: codestyle unittests
+.PHONY: codestyle codestyle-fix unittests deps build dist clean

+ 2 - 0
frontend/package.json

@@ -11,6 +11,8 @@
 		"stylelint-config-standard": "^40.0.0"
 	},
 	"scripts": {
+		"lint": "eslint main.js js/* resources/vue vite.config.mjs && stylelint \"style.css\" \"themes/**/*.css\"",
+		"lint:fix": "eslint --fix main.js js/* resources/vue vite.config.mjs && stylelint --fix \"style.css\" \"themes/**/*.css\"",
 		"test": "node --test resources/vue/components/*.test.mjs resources/vue/utils/*.test.mjs resources/vue/stores/*.test.mjs"
 	},
 	"author": "",

+ 12 - 12
frontend/vite.config.mjs

@@ -4,16 +4,16 @@ import Components from 'unplugin-vue-components/vite'
 
 export default defineConfig({
   resolve: {
-    dedupe: ['vue', 'vue-router'],
+    dedupe: ['vue', 'vue-router']
   },
   plugins: [
     Components({
       dirs: ['resources/vue/'],
       extensions: ['vue'],
       deep: true,
-      dts: false,
+      dts: false
     }),
-    vue(),
+    vue()
   ],
   build: {
     rolldownOptions: {
@@ -22,25 +22,25 @@ export default defineConfig({
           return
         }
         defaultHandler(level, log)
-      },
-    },
+      }
+    }
   },
   server: {
     proxy: {
       '/api': {
         target: 'http://localhost:1337',
         changeOrigin: true,
-        secure: false,
+        secure: false
       },
       '/theme.css': {
         target: 'http://localhost:1337',
         changeOrigin: true,
-        secure: false,
+        secure: false
       },
-      "/custom-webui": {
-        target: "http://localhost:1337",
-        changeOrigin: true,
+      '/custom-webui': {
+        target: 'http://localhost:1337',
+        changeOrigin: true
       }
-    },
-  },
+    }
+  }
 })

+ 0 - 8
integration-tests/.eslintrc.yml

@@ -1,8 +0,0 @@
-env:
-  browser: true
-  es2021: true
-extends: 'eslint:recommended'
-parserOptions:
-  ecmaVersion: 12
-  sourceType: module
-rules: {}

+ 9 - 3
integration-tests/Makefile

@@ -1,7 +1,13 @@
 default: test-install prep test-run
 
 test-install:
-	npm install --no-fund
+	npm ci --no-fund
+
+codestyle: test-install
+	npm run lint
+
+codestyle-fix: test-install
+	npm run lint:fix
 
 prep:
 ifneq ($(SKIP_WEBUI),1)
@@ -11,7 +17,7 @@ endif
 
 test-run:
     # GitHub Actions fails badly on the default timeout of 2000ms
-	npx mocha tests --recursive -t 10000
+	npm test
 
 find-flakey-tests:
 	echo "Running test-run infinately"
@@ -30,4 +36,4 @@ getsnapshot:
 	rm -rf /opt/OliveTin-snapshot/*
 	gh run download -D /opt/OliveTin-snapshot/
 
-.PHONY: default find-flakey-tests find-flakey-tests-inf prep
+.PHONY: default test-install codestyle codestyle-fix test-run find-flakey-tests find-flakey-tests-inf prep

+ 31 - 0
integration-tests/eslint.config.mjs

@@ -0,0 +1,31 @@
+import js from '@eslint/js'
+import globals from 'globals'
+
+export default [
+  {
+    ignores: [
+      'node_modules/**',
+      'tests/customJs/custom-webui/**'
+    ]
+  },
+  js.configs.recommended,
+  {
+    files: ['**/*.{js,mjs}'],
+    languageOptions: {
+      ecmaVersion: 'latest',
+      sourceType: 'module',
+      globals: {
+        ...globals.browser,
+        ...globals.node,
+        after: 'readonly',
+        afterEach: 'readonly',
+        before: 'readonly',
+        beforeEach: 'readonly',
+        describe: 'readonly',
+        it: 'readonly',
+        runner: 'readonly',
+        webdriver: 'readonly'
+      }
+    }
+  }
+]

+ 1 - 1
integration-tests/lib/elements.js

@@ -114,7 +114,7 @@ export function takeScreenshot (webdriver, title) {
     fs.mkdirSync('screenshots', { recursive: true });
 
   title = title.replaceAll('config: ', '')
-	title = title.replaceAll(/[\(\)\|\*\<\>\:]/g, "_")
+	title = title.replaceAll(/[()|*<>:]/g, '_')
 	title = title + '.failed-test'
 
     fs.writeFileSync('screenshots/' + title + '.png', img, 'base64')

+ 36 - 0
integration-tests/package-lock.json

@@ -12,8 +12,10 @@
         "wait-on": "^9.1.0"
       },
       "devDependencies": {
+        "@eslint/js": "^10.0.1",
         "chai": "^6.2.2",
         "eslint": "^10.10.0",
+        "globals": "^17.3.0",
         "mocha": "^12.0.0",
         "selenium-webdriver": "^4.49.0"
       }
@@ -141,6 +143,27 @@
         "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
+    "node_modules/@eslint/js": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@eslint/object-schema": {
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
@@ -966,6 +989,19 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/globals": {
+      "version": "17.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz",
+      "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/gopd": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",

+ 5 - 1
integration-tests/package.json

@@ -6,13 +6,17 @@
   "main": "index.js",
   "type": "module",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "lint": "eslint tests lib scripts runner.mjs mochaSetup.mjs",
+    "lint:fix": "npm run lint -- --fix",
+    "test": "mocha tests --recursive -t 10000"
   },
   "author": "",
   "license": "AGPL-3.0-only",
   "devDependencies": {
+    "@eslint/js": "^10.0.1",
     "chai": "^6.2.2",
     "eslint": "^10.10.0",
+    "globals": "^17.3.0",
     "mocha": "^12.0.0",
     "selenium-webdriver": "^4.49.0"
   },

+ 1 - 1
integration-tests/scripts/find-flakey-tests-inf.mjs

@@ -104,7 +104,7 @@ function runMochaOnce () {
       try {
         report = JSON.parse(readFileSync(reportPath, 'utf8'))
       } catch {
-        report = null
+        // Keep the default null report when Mocha did not produce valid JSON.
       }
 
       try {

+ 1 - 1
integration-tests/tests/argumentActionFlash/argumentActionFlash.mjs

@@ -28,7 +28,7 @@ async function waitForActionSuccessFlash (actionTitle) {
         const button = await getActionButton(webdriver, actionTitle)
         const classAttr = await button.getAttribute('class')
         return classAttr && classAttr.includes('action-success')
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 1 - 1
integration-tests/tests/argumentIdCollision/argumentIdCollision.mjs

@@ -45,7 +45,7 @@ async function waitForTerminalOutput (expectedSubstring) {
 
         const output = await getTerminalBuffer()
         return output && output.includes(expectedSubstring)
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 1 - 2
integration-tests/tests/authRequireGuestsToLogin/authRequireGuestsToLogin.mjs

@@ -1,8 +1,7 @@
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, until } from 'selenium-webdriver'
+import { By } from 'selenium-webdriver'
 import {
-  getRootAndWait,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 

+ 1 - 1
integration-tests/tests/checkbox/checkbox.mjs

@@ -47,7 +47,7 @@ async function waitForTerminalOutput(expectedValue) {
         }
 
         return output.trim().includes(`Checkbox value: ${expectedValue}`)
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 1 - 1
integration-tests/tests/checklist/checklist.mjs

@@ -44,7 +44,7 @@ async function pollTerminal(matcher, timeoutMs = DEFAULT_UI_WAIT_MS) {
         }
 
         return matcher(output.trim())
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 1 - 1
integration-tests/tests/confirmation/confirmation.mjs

@@ -59,7 +59,7 @@ async function waitForTerminalOutput (expectedSubstring) {
 
         const output = await getTerminalBuffer()
         return output && output.includes(expectedSubstring)
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 1 - 1
integration-tests/tests/dashboardsWithBasicFieldsets/dashboardsWithBasicFieldsets.mjs

@@ -1,6 +1,6 @@
 import { describe, it, before, after } from 'mocha'
 import { expect, assert } from 'chai'
-import { By, until, Condition } from 'selenium-webdriver'
+import { By } from 'selenium-webdriver'
 //import * as waitOn from 'wait-on'
 import {
   getRootAndWait,

+ 0 - 1
integration-tests/tests/emptyDashboardsAreHidden/emptyDashboardsAreHidden.mjs

@@ -1,6 +1,5 @@
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, until, Condition } from 'selenium-webdriver'
 //import * as waitOn from 'wait-on'
 import {
   getRootAndWait,

+ 1 - 3
integration-tests/tests/hiddenFooter/hiddenFooter.mjs

@@ -2,9 +2,7 @@ import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
 
 import { By } from 'selenium-webdriver'
-import { 
-  getRootAndWait, 
-  getActionButtons,
+import {
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 

+ 2 - 5
integration-tests/tests/hiddenNav/hiddenNav.mjs

@@ -1,8 +1,5 @@
-import { expect } from 'chai'
-import { By } from 'selenium-webdriver'
-import { 
-  getRootAndWait, 
-  getActionButtons,
+import {
+  getRootAndWait,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 

+ 1 - 1
integration-tests/tests/justification/justification.mjs

@@ -88,7 +88,7 @@ async function waitForTerminalOutput (expectedSubstring) {
 
         const output = await getTerminalBuffer()
         return output && output.includes(expectedSubstring)
-      } catch (e) {
+      } catch {
         return false
       }
     }),

+ 5 - 6
integration-tests/tests/localAuth/localAuth.mjs

@@ -1,8 +1,7 @@
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, until, Condition } from 'selenium-webdriver'
+import { By, until } from 'selenium-webdriver'
 import {
-  getRootAndWait,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 
@@ -47,7 +46,7 @@ describe('config: localAuth', function () {
     // Check if any login-related elements are present
     const bodyText = await webdriver.findElement(By.tagName('body')).getText()
     console.log('Login page content:', bodyText.substring(0, 300))
-    
+
     // For now, just verify we can navigate to the login page
     // The page content rendering is a separate frontend issue
     console.log('Login page navigation successful')
@@ -67,11 +66,11 @@ describe('config: localAuth', function () {
 
     if (usernameFields.length > 0 && passwordFields.length > 0 && loginButtons.length > 0) {
       console.log('Login form found, attempting login')
-      
+
       // Fill in credentials
       await usernameFields[0].clear()
       await usernameFields[0].sendKeys('testuser')
-      
+
       await passwordFields[0].clear()
       await passwordFields[0].sendKeys('testpass123')
 
@@ -100,4 +99,4 @@ describe('config: localAuth', function () {
       console.log('Login form not found - skipping login test')
     }
   })
-})
+})

+ 1 - 2
integration-tests/tests/logPersistence/logPersistence.mjs

@@ -5,7 +5,6 @@ import fs from 'fs'
 import path from 'path'
 import {
   getRootAndWait,
-  getActionButtons,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 
@@ -210,7 +209,7 @@ describe('config: logPersistence', function () {
           const text = await body.getText()
           // The log should contain the output from the echo command
           return text.includes('Hello from persisted log test') || text.includes(firstExecutionTrackingId)
-        } catch (e) {
+        } catch {
           return false
         }
       }),

+ 1 - 1
integration-tests/tests/multipleDropdowns/multipleDropdowns.mjs

@@ -1,6 +1,6 @@
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, until, Condition, Key } from 'selenium-webdriver'
+import { By, Condition, Key } from 'selenium-webdriver'
 import {
   getRootAndWait,
   getActionButtons,

+ 8 - 15
integration-tests/tests/oauthLoginGithub/githubOAuth.mjs

@@ -1,8 +1,7 @@
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, until, Condition } from 'selenium-webdriver'
+import { By, until } from 'selenium-webdriver'
 import {
-  getRootAndWait,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 
@@ -56,14 +55,14 @@ describe('config: githubOAuth', function () {
     // Button may show "Login with GitHub" or "Login with undefined" depending on provider.name vs provider.title
     // We'll check for the presence of the button and verify it's in the OAuth section
     expect(githubButtons.length).to.be.greaterThan(0, 'At least one OAuth button should be present')
-    
+
     // The first button should be GitHub since it's the only provider in the config
     const githubButton = githubButtons[0]
     const buttonText = await githubButton.getText()
-    
+
     // Button should contain "Login with" and the provider should be configured as GitHub
     expect(buttonText).to.include('Login with', 'Button should have "Login with" prefix')
-    
+
     console.log('GitHub OAuth button found with text:', buttonText)
   })
 
@@ -78,16 +77,14 @@ describe('config: githubOAuth', function () {
     // Since the test config only has one provider (GitHub), we can use the first button
     const githubButtons = await webdriver.findElements(By.css('.oauth-button'))
     expect(githubButtons.length).to.be.greaterThan(0, 'At least one OAuth button should be present')
-    
+
     const githubButton = githubButtons[0]
     const buttonText = await githubButton.getText()
     console.log('Button text:', buttonText)
-    
+
     // Verify it's the GitHub button (should contain "github" in the text)
     expect(buttonText.toLowerCase()).to.include('github', 'Button should be GitHub OAuth button')
 
-    // Check for provider icon (if present)
-    const providerIcons = await githubButton.findElements(By.css('.provider-icon'))
     const providerNames = await githubButton.findElements(By.css('.provider-name'))
     // Provider name may show "GitHub" (from title) or be undefined (if using name field)
     // Just verify the structure is present
@@ -111,11 +108,8 @@ describe('config: githubOAuth', function () {
     // Find GitHub OAuth button (should be the first/only one in our test config)
     const githubButtons = await webdriver.findElements(By.css('.oauth-button'))
     expect(githubButtons.length).to.be.greaterThan(0, 'OAuth button should be present')
-    
-    const githubButton = githubButtons[0]
 
-    // Get the current URL before clicking
-    const initialUrl = await webdriver.getCurrentUrl()
+    const githubButton = githubButtons[0]
 
     // Click the button
     await githubButton.click()
@@ -124,7 +118,7 @@ describe('config: githubOAuth', function () {
     // Since we can't actually complete OAuth flow, we check that the button
     // click handler is set up correctly by verifying the button exists and is clickable
     // In a real scenario, this would redirect to GitHub's OAuth page
-    
+
     // Give a small delay to allow any navigation to start
     await new Promise(resolve => setTimeout(resolve, 1000))
 
@@ -133,4 +127,3 @@ describe('config: githubOAuth', function () {
     console.log('GitHub OAuth button click verified (redirect would happen in production)')
   })
 })
-

+ 1 - 2
integration-tests/tests/onlyDashboards/onlyDashboards.mjs

@@ -6,7 +6,6 @@ import {
   getActionButtons,
   getNavigationLinks,
   openSidebar,
-  closeSidebar,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 
@@ -43,7 +42,7 @@ describe('config: onlyDashboards', function () {
     const firstDashboardLink = await webdriver.findElement(By.css('li[title="My Dashboard"]'), 'The first dashboard link should be present')
     assert.isNotNull(firstDashboardLink, 'First dashboard link should not be null')
     assert.isTrue(await firstDashboardLink.isDisplayed(), 'First dashboard link should be displayed')
-    
+
     const actionButtonsOnDashboard = await getActionButtons()
     assert.isArray(actionButtonsOnDashboard, 'Action buttons on dashboard should be an array')
     assert.lengthOf(actionButtonsOnDashboard, 3, 'Action buttons on dashboard should have 3 buttons')

+ 1 - 3
integration-tests/tests/sleep/sleep.mjs

@@ -1,9 +1,7 @@
-import * as process from 'node:process'
 import { describe, it, before, after } from 'mocha'
 import { expect } from 'chai'
-import { By, Condition } from 'selenium-webdriver'
+import { By } from 'selenium-webdriver'
 import {
-  takeScreenshot,
   takeScreenshotOnFailure,
   findExecutionDialog,
   requireExecutionDialogStatus,

+ 4 - 5
integration-tests/tests/stdoutMostRecentExecution/stdoutMostRecentExecution.mjs

@@ -3,7 +3,6 @@ import { expect } from 'chai'
 import { By, Condition } from 'selenium-webdriver'
 import {
   getRootAndWait,
-  getActionButtons,
   takeScreenshotOnFailure,
 } from '../../lib/elements.js'
 
@@ -116,13 +115,13 @@ describe('config: stdout-most-recent-execution', function () {
           // Output should change from initial state and contain actual output
           // (not "Waiting...", "No execution found", or the same as initialText)
           const hasChanged = newText !== initialText
-          const hasValidOutput = newText && 
-                                 !newText.includes('Waiting...') && 
-                                 !newText.includes('No execution found') && 
+          const hasValidOutput = newText &&
+                                 !newText.includes('Waiting...') &&
+                                 !newText.includes('No execution found') &&
                                  !newText.includes('Error:') &&
                                  newText.trim().length > 0
           return hasChanged && hasValidOutput
-        } catch (e) {
+        } catch {
           return false
         }
       }),