From b875f1b9dda2bbed3bbdf31cc99184f955b4be42 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Fri, 4 Nov 2022 17:02:43 +0000 Subject: [PATCH 01/14] Enable TypeScript --- .eslintrc.cjs | 8 ++ README.md | 2 +- cypress.config.cjs | 2 +- package.json | 3 + renovate.json | 9 +- tsconfig.json | 29 +++++ vite.config.js => vite.config.ts | 0 yarn.lock | 186 +++++++++++++++++++++++++++++-- 8 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 tsconfig.json rename vite.config.js => vite.config.ts (100%) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e5a316339..e80491fec 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -62,4 +62,12 @@ module.exports = { 'off' ] }, + overrides: [ + { + files: ['*.ts'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + extends: ['plugin:@typescript-eslint/recommended'] + } + ] } diff --git a/README.md b/README.md index 9ce5b468f..d68f383ad 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ The project was originally created with [vue-cli](https://cli.vuejs.org/), but has switched to [Vite](https://vitejs.dev/) with the upgrade from Vue 2 to 3. The configuration for how the app is served and built is defined in -[`vite.config.js`](vite.config.js). +[`vite.config.ts`](vite.config.ts). We are currently using the [Vuetify component library](https://vuetifyjs.com/en/). Its configuration is defined in [`src/plugins/vuetify.js`](src/plugins/vuetify.js). diff --git a/cypress.config.cjs b/cypress.config.cjs index d09185c4a..cfa9a7240 100644 --- a/cypress.config.cjs +++ b/cypress.config.cjs @@ -29,7 +29,7 @@ module.exports = defineConfig({ on( 'file:preprocessor', vitePreprocessor({ - configFile: path.resolve(__dirname, './vite.config.js'), + configFile: path.resolve(__dirname, './vite.config.ts'), mode: 'development', }) ) diff --git a/package.json b/package.json index 9e2c72365..342756c54 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,8 @@ }, "devDependencies": { "@cypress/code-coverage": "3.12.29", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "@vitejs/plugin-vue": "5.0.4", "@vitest/coverage-istanbul": "1.4.0", "@vue/test-utils": "2.4.5", @@ -82,6 +84,7 @@ "sass": "1.71.1", "sinon": "17.0.1", "standard": "17.1.0", + "typescript": "5.3.3", "vite": "5.1.6", "vite-plugin-eslint": "1.8.1", "vite-plugin-istanbul": "6.0.0", diff --git a/renovate.json b/renovate.json index 6331f5185..cdc50ffbd 100644 --- a/renovate.json +++ b/renovate.json @@ -6,6 +6,10 @@ "platformAutomerge": true, "automergeStrategy": "squash", "packageRules": [ + { + "matchPackageNames": ["typescript"], + "automerge": true + }, { "matchPackageNames": ["yarn"], "automerge": true @@ -78,7 +82,10 @@ { "groupName": "eslint packages", "matchPackageNames": ["standard"], - "matchPackagePatterns": ["^eslint"], + "matchPackagePatterns": [ + "^eslint", + "^@typescript-eslint" + ], "automerge": true, "schedule": ["on the first day of the month"] }, diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..626b1ce1b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "include": [ + "src/**/*.ts", + "src/**/*.vue", + // "tests/**/*" + ], + "exclude": [ + "src/services/mock/json/*" + ], + "compilerOptions": { + // "checkJs": true, + "paths": { + "@/*": ["./src/*"] + }, + + "allowJs": true, + + // Vue recommended settings: + "isolatedModules": true, + "strict": true, + + // // if using webpack 2+ or rollup, to leverage tree shaking: + // "module": "es2015", + // "moduleResolution": "node" + }, + "watchOptions": { + "excludeFiles": ["**/node_modules"] + } +} diff --git a/vite.config.js b/vite.config.ts similarity index 100% rename from vite.config.js rename to vite.config.ts diff --git a/yarn.lock b/yarn.lock index 2dbd36159..b4cefe0b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -646,7 +646,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.6.0, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.0, @eslint-community/regexpp@npm:^4.6.1": version: 4.10.0 resolution: "@eslint-community/regexpp@npm:4.10.0" checksum: 10c0/c5f60ef1f1ea7649fa7af0e80a5a79f64b55a8a8fa5086de4727eb4c86c652aedee407a9c143b8995d2c0b2d75c1222bec9ba5d73dbfc1f314550554f0979ef4 @@ -1995,7 +1995,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db @@ -2018,6 +2018,13 @@ __metadata: languageName: node linkType: hard +"@types/semver@npm:^7.5.0": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa + languageName: node + linkType: hard + "@types/sinonjs__fake-timers@npm:8.1.1": version: 8.1.1 resolution: "@types/sinonjs__fake-timers@npm:8.1.1" @@ -2057,6 +2064,129 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.1.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.5.1" + "@typescript-eslint/scope-manager": "npm:7.1.0" + "@typescript-eslint/type-utils": "npm:7.1.0" + "@typescript-eslint/utils": "npm:7.1.0" + "@typescript-eslint/visitor-keys": "npm:7.1.0" + debug: "npm:^4.3.4" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.2.4" + natural-compare: "npm:^1.4.0" + semver: "npm:^7.5.4" + ts-api-utils: "npm:^1.0.1" + peerDependencies: + "@typescript-eslint/parser": ^7.0.0 + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/e5644a987969cbb614bbf766b6bf51341e123c774953690548610147eae0041d70e48ef42be97b68a6e2f5ed9aae37fe040e8054d35bb0568c14194ba564b2d8 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/parser@npm:7.1.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:7.1.0" + "@typescript-eslint/types": "npm:7.1.0" + "@typescript-eslint/typescript-estree": "npm:7.1.0" + "@typescript-eslint/visitor-keys": "npm:7.1.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/8fcbfc8c0c86abb750173096e7ca09e1cd44aba3f6115bdb94ffb6b409b86ee23526e9d5a44935b69a6be2385893e66d8e55d92063206028dc48f70d379afcab + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/scope-manager@npm:7.1.0" + dependencies: + "@typescript-eslint/types": "npm:7.1.0" + "@typescript-eslint/visitor-keys": "npm:7.1.0" + checksum: 10c0/2fd167730bbe984343ab94739b00bd82e8cdeea9e63674b099cc5c89b420b28dbf79f40dab48022dc717db8d14ae6ee2739e0fcbdcc0321bc9da5f2602b55788 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/type-utils@npm:7.1.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:7.1.0" + "@typescript-eslint/utils": "npm:7.1.0" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^1.0.1" + peerDependencies: + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/3e3eea6c03692a643bf4ed11646b0679c6ff13baf1647d97e793f3d8c3adb83061e27a17c2a1470166a3c6c444b974bebc8096d36e0b4b3c36c289ff38bcfc9b + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/types@npm:7.1.0" + checksum: 10c0/095cde3e773b7605c5e0c86642002768ced09e94def7f3c6f49a67863f47d7c8ae15413a4ab1a2407f779d1b5ede5fb3000bc98b1cf9ed7ec938acc38cac89e7 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.1.0" + dependencies: + "@typescript-eslint/types": "npm:7.1.0" + "@typescript-eslint/visitor-keys": "npm:7.1.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + minimatch: "npm:9.0.3" + semver: "npm:^7.5.4" + ts-api-utils: "npm:^1.0.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/063845dc8526dfda722d1b00960443a5158d1bce2bc39bf49bd353f33f42aa30116105a87b55a04df3eaef99c0d1c13fb987c53848dff43de6152c66dd3ba41c + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/utils@npm:7.1.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@types/json-schema": "npm:^7.0.12" + "@types/semver": "npm:^7.5.0" + "@typescript-eslint/scope-manager": "npm:7.1.0" + "@typescript-eslint/types": "npm:7.1.0" + "@typescript-eslint/typescript-estree": "npm:7.1.0" + semver: "npm:^7.5.4" + peerDependencies: + eslint: ^8.56.0 + checksum: 10c0/3fefd51307d0e294462106c57c4b12cd610bfe1bdcc5ca0142bfac6a5d0d37c18d14be5ec89740eb85515f5512f45219a6048df0efccd457e96f9d0612af4abf + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:7.1.0": + version: 7.1.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.1.0" + dependencies: + "@typescript-eslint/types": "npm:7.1.0" + eslint-visitor-keys: "npm:^3.4.1" + checksum: 10c0/9015a10e6ee2a99fc99e0f7a3f274496a813c2c239e868f29e7c0da919c825fe192fe21d3410c43d8a801e8186b51f08ef06523d2c3010570d893a1486ac293d + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -3568,6 +3698,8 @@ __metadata: "@lumino/default-theme": "npm:2.1.4" "@lumino/widgets": "npm:2.3.1" "@mdi/js": "npm:7.4.47" + "@typescript-eslint/eslint-plugin": "npm:7.1.0" + "@typescript-eslint/parser": "npm:7.1.0" "@unhead/vue": "npm:1.8.10" "@vitejs/plugin-vue": "npm:5.0.4" "@vitest/coverage-istanbul": "npm:1.4.0" @@ -3611,6 +3743,7 @@ __metadata: standard: "npm:17.1.0" subscriptions-transport-ws: "npm:0.11.0" svg-pan-zoom: "npm:3.6.1" + typescript: "npm:5.3.3" vite: "npm:5.1.6" vite-plugin-eslint: "npm:1.8.1" vite-plugin-istanbul: "npm:6.0.0" @@ -5536,7 +5669,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:11.1.0": +"globby@npm:11.1.0, globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -7181,6 +7314,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.3, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -7190,15 +7332,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac - languageName: node - linkType: hard - "minimist@npm:^1.2.0, minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -9599,6 +9732,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^1.0.1": + version: 1.3.0 + resolution: "ts-api-utils@npm:1.3.0" + peerDependencies: + typescript: ">=4.2.0" + checksum: 10c0/f54a0ba9ed56ce66baea90a3fa087a484002e807f28a8ccb2d070c75e76bde64bd0f6dce98b3802834156306050871b67eec325cb4e918015a360a3f0868c77c + languageName: node + linkType: hard + "ts-invariant@npm:^0.10.3": version: 0.10.3 resolution: "ts-invariant@npm:0.10.3" @@ -9753,6 +9895,26 @@ __metadata: languageName: node linkType: hard +"typescript@npm:5.3.3": + version: 5.3.3 + resolution: "typescript@npm:5.3.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.3.3#optional!builtin": + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500 + languageName: node + linkType: hard + "uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": version: 1.0.6 resolution: "uc.micro@npm:1.0.6" From 12f486052fb398ca37a3e37f1a33b99ce88d64ab Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:20:16 +0000 Subject: [PATCH 02/14] Convert a couple of files to TypeScript --- .../cylc/common/{sort.js => sort.ts} | 46 ++++++++----------- .../cylc/table/{sort.js => sort.ts} | 8 ++-- 2 files changed, 23 insertions(+), 31 deletions(-) rename src/components/cylc/common/{sort.js => sort.ts} (62%) rename src/components/cylc/table/{sort.js => sort.ts} (80%) diff --git a/src/components/cylc/common/sort.js b/src/components/cylc/common/sort.ts similarity index 62% rename from src/components/cylc/common/sort.js rename to src/components/cylc/common/sort.ts index 56584c5e4..a8a24a141 100644 --- a/src/components/cylc/common/sort.js +++ b/src/components/cylc/common/sort.ts @@ -15,27 +15,25 @@ * along with this program. If not, see . */ +type Obj = string | Record + /** * Declare function used in sortedIndexBy as a comparator. * - * @callback SortedIndexByComparator - * @param {Object} leftObject - left parameter object - * @param {string} leftValue - left parameter value - * @param {Object} rightObject - right parameter object - * @param {string} rightValue - right parameter value - * @returns {boolean} - true if leftValue is higher than rightValue + * @returns positive int if leftValue is higher than rightValue */ +type SortedIndexByComparator = (leftObject: Obj, leftValue: string, rightObject: Obj, rightValue: string) => number + +type SortedIndexOptions = { + comparator?: SortedIndexByComparator + reverse?: boolean +} /** * The default comparator used to compare strings for cycle points, family proxies names, * task proxies names, and jobs. - * - * @param {string} left - * @param {string} right - * @returns {number} - * @constructor */ -export const DEFAULT_COMPARATOR = (left, right) => { +export const DEFAULT_COMPARATOR = (left: string, right: string): number => { return left.toLowerCase() .localeCompare( right.toLowerCase(), @@ -49,11 +47,8 @@ export const DEFAULT_COMPARATOR = (left, right) => { /** * Declare function used in sortedIndexBy for creating the iteratee. - * - * @callback SortedIndexByIteratee - * @param {Object} value - any object - * @returns {string} */ +type SortedIndexByIteratee = (value: string | Record) => string /** * Given a list of elements, and a value to be added to the list, we @@ -68,29 +63,26 @@ export const DEFAULT_COMPARATOR = (left, right) => { * name, but that respects natural order for numbers, i.e. [1, 2, 10]. * Not [1, 10, 2]. * - * @param {Object[]} array - list of string values, or of objects with string values - * @param {Object} value - a value to be inserted in the list, or an object wrapping the value (see iteratee) - * @param {SortedIndexByIteratee=} iteratee - an optional function used to return the value of the element of the list} - * @param {SortedIndexByComparator=} comparator - function used to compare the newValue with otherValues in the list - * @return {number} - sorted index + * @param array - list of string values, or of objects with string values + * @param value - a value to be inserted in the list, or an object wrapping the value (see iteratee) + * @param iteratee - an optional function used to return the value of the element of the list + * @param comparator - function used to compare the newValue with otherValues in the list + * @return sorted index */ -export function sortedIndexBy (array, value, iteratee, options = {}) { - // comparator, reverse = false) { +export function sortedIndexBy (array: Obj[], value: Obj, iteratee: SortedIndexByIteratee, options: SortedIndexOptions = {}): number { if (array.length === 0) { return 0 } - // If given a function, use it. Otherwise, simply use identity function. - const iterateeFunction = iteratee || ((value) => value) // If given a function, use it. Otherwise, simply use locale sort with numeric enabled const comparatorFunction = options.comparator || ((leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue)) let low = 0 let high = array.length - const newValue = iterateeFunction(value) + const newValue = iteratee(value) while (low < high) { const mid = Math.floor((low + high) / 2) - const midValue = iterateeFunction(array[mid]) + const midValue = iteratee(array[mid]) let higher = comparatorFunction(value, newValue, array[mid], midValue) if (options.reverse) { higher = higher * -1 diff --git a/src/components/cylc/table/sort.js b/src/components/cylc/table/sort.ts similarity index 80% rename from src/components/cylc/table/sort.js rename to src/components/cylc/table/sort.ts index 09b5b4ccb..8f3f29c57 100644 --- a/src/components/cylc/table/sort.js +++ b/src/components/cylc/table/sort.ts @@ -20,11 +20,11 @@ * strings are treated as infinity. * * @export - * @param {*} a - The first element for comparison. - * @param {*} b - The second element for comparison. - * @return {number} A number > 0 if a > b, or < 0 if a < b, or 0 if a === b + * @param a - The first element for comparison. + * @param b - The second element for comparison. + * @return A number > 0 if a > b, or < 0 if a < b, or 0 if a === b */ -export function datetimeComparator (a, b) { +export function datetimeComparator (a: string | number, b: string | number): number { a = (a ?? '') === '' ? Infinity : new Date(a).getTime() b = (b ?? '') === '' ? Infinity : new Date(b).getTime() // Avoid return NaN for a === b === Infinity From b94decef57edc687211554c02c2261b5f4567868 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:55:26 +0000 Subject: [PATCH 03/14] Fixup --- .eslintrc.cjs | 19 ++++++++++--------- tsconfig.json | 4 +++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e80491fec..15310f3b4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -17,10 +17,15 @@ module.exports = { root: true, + plugins: [ + '@typescript-eslint', + ], + parser: 'vue-eslint-parser', parserOptions: { /* Allow new ECMAScript syntax but not globals. This is because vite/esbuild transforms syntax to es2015 (at the earliest) but does not pollyfill APIs. */ ecmaVersion: 'latest', + parser: '@typescript-eslint/parser' }, extends: [ 'standard', @@ -28,6 +33,7 @@ module.exports = { 'plugin:vue/vue3-essential', 'plugin:vuetify/base', 'plugin:cypress/recommended', + 'plugin:@typescript-eslint/recommended', ], rules: { 'comma-dangle': [ @@ -60,14 +66,9 @@ module.exports = { ], 'cypress/unsafe-to-chain-command': [ 'off' - ] + ], + '@typescript-eslint/no-unused-vars': [ + 'off' // handled by typescript itself + ], }, - overrides: [ - { - files: ['*.ts'], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: ['plugin:@typescript-eslint/recommended'] - } - ] } diff --git a/tsconfig.json b/tsconfig.json index 626b1ce1b..e87f6b6ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,9 +15,11 @@ "allowJs": true, + "noUnusedLocals": true, + // Vue recommended settings: "isolatedModules": true, - "strict": true, + "strict": true // // if using webpack 2+ or rollup, to leverage tree shaking: // "module": "es2015", From b2c62536e46a68572acdffdaaa522f520e39fd5b Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Mon, 4 Mar 2024 11:26:35 +0000 Subject: [PATCH 04/14] fixup! Enable TypeScript --- .eslintrc.cjs | 19 ++++++++++++++++--- .github/workflows/main.yml | 4 ++++ cypress.config.cjs | 1 + package.json | 3 ++- scripts/concurrently.cjs | 3 ++- src/services/mock/json-server.cjs | 1 + src/services/mock/json/index.cjs | 4 ++-- src/services/mock/websockets.cjs | 1 + tsconfig.json | 7 ++++--- 9 files changed, 33 insertions(+), 10 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 15310f3b4..8b73c5e1d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -49,6 +49,22 @@ module.exports = { 'template-curly-spacing': [ 'off' ], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'none', + } + ], + '@typescript-eslint/no-var-requires': [ + 'error', + { + allow: [ + '.*\\.cjs', + '.*\\.json', + ], + } + ], 'vue/multi-word-component-names': [ 'off' ], @@ -67,8 +83,5 @@ module.exports = { 'cypress/unsafe-to-chain-command': [ 'off' ], - '@typescript-eslint/no-unused-vars': [ - 'off' // handled by typescript itself - ], }, } diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 45deb3ac1..758140387 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,10 @@ jobs: run: | yarn run lint + - name: Type check + run: | + yarn run type-check + - name: Test run: | yarn run coverage:unit diff --git a/cypress.config.cjs b/cypress.config.cjs index cfa9a7240..33c8636f2 100644 --- a/cypress.config.cjs +++ b/cypress.config.cjs @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ const { defineConfig } = require('cypress') const vitePreprocessor = require('cypress-vite') const path = require('path') diff --git a/package.json b/package.json index 342756c54..e9b9166ca 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test:component": "cypress open --component", "test:e2e": "yarn run serve e2e:open", "test:unit": "vitest", - "test": "vitest run && yarn run serve cy:run" + "test": "vitest run && yarn run serve cy:run", + "type-check": "tsc --noEmit" }, "dependencies": { "@apollo/client": "3.9.4", diff --git a/scripts/concurrently.cjs b/scripts/concurrently.cjs index 2a5f9bda2..aa9ae2f57 100644 --- a/scripts/concurrently.cjs +++ b/scripts/concurrently.cjs @@ -1,4 +1,5 @@ -/* eslint-disable no-console */ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable no-console */ const concurrently = require('concurrently') const args = process.argv.slice(2) diff --git a/src/services/mock/json-server.cjs b/src/services/mock/json-server.cjs index 81dd0ca61..02ff63a9f 100644 --- a/src/services/mock/json-server.cjs +++ b/src/services/mock/json-server.cjs @@ -18,6 +18,7 @@ // TODO: make it configurable const PORT = 3000 +/* eslint-disable @typescript-eslint/no-var-requires */ const userProfile = require('./json/userprofile.json') const graphql = require('./graphql.cjs') const websockets = require('./websockets.cjs') diff --git a/src/services/mock/json/index.cjs b/src/services/mock/json/index.cjs index 2ab94d326..e8e06a40d 100644 --- a/src/services/mock/json/index.cjs +++ b/src/services/mock/json/index.cjs @@ -19,8 +19,8 @@ const IntrospectionQuery = require('./IntrospectionQuery.json') const userProfile = require('./userprofile.json') const taskProxy = require('./taskProxy.json') const familyProxy = require('./familyProxy.json') -const workflowOne = require('./workflows/one') -const workflowsMulti = require('./workflows/multi') +const workflowOne = require('./workflows/one.json') +const workflowsMulti = require('./workflows/multi.json') const { LogData } = require('./logData.cjs') const { LogFiles } = require('./logFiles.cjs') const analysisQuery = require('./analysisQuery.json') diff --git a/src/services/mock/websockets.cjs b/src/services/mock/websockets.cjs index a81d0dcd0..0455a5d3d 100644 --- a/src/services/mock/websockets.cjs +++ b/src/services/mock/websockets.cjs @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +/* eslint-disable @typescript-eslint/no-var-requires */ const { isArray } = require('lodash') const graphql = require('./graphql.cjs') diff --git a/tsconfig.json b/tsconfig.json index e87f6b6ea..ad456906b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,8 +15,6 @@ "allowJs": true, - "noUnusedLocals": true, - // Vue recommended settings: "isolatedModules": true, "strict": true @@ -26,6 +24,9 @@ // "moduleResolution": "node" }, "watchOptions": { - "excludeFiles": ["**/node_modules"] + "excludeDirectories": [ + "**/node_modules", + "dist" + ] } } From 1725e9850c7bb286470e260823657959d9ddb4e8 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:22:36 +0000 Subject: [PATCH 05/14] Convert some files to TypeScript --- src/App.vue | 2 +- src/components/core/Alert.vue | 7 ++-- .../cylc/common/{filter.js => filter.ts} | 31 +++++++---------- src/components/cylc/common/sort.ts | 18 +++++----- src/components/cylc/gscan/GScan.vue | 2 +- .../cylc/gscan/{filters.js => filters.ts} | 34 ++++++++++--------- .../cylc/gscan/{sort.js => sort.ts} | 0 src/components/cylc/workflow/Lumino.vue | 2 +- src/components/cylc/workflow/Toolbar.vue | 2 +- .../{localStorage.js => localStorage.ts} | 0 src/plugins/{vuetify.js => vuetify.ts} | 13 +++---- src/utils/{font-size.js => font-size.ts} | 10 +++--- src/utils/{index.js => index.ts} | 23 +++++++------ src/views/UserProfile.vue | 2 +- src/views/Workspace.vue | 2 +- src/views/{views.js => views.ts} | 20 ++++++----- .../components/cylc/gscan/gscan.vue.spec.js | 2 +- .../cylc/workflow/toolbar.vue.spec.js | 2 +- tests/unit/views/views.spec.js | 2 +- 19 files changed, 90 insertions(+), 84 deletions(-) rename src/components/cylc/common/{filter.js => filter.ts} (71%) rename src/components/cylc/gscan/{filters.js => filters.ts} (68%) rename src/components/cylc/gscan/{sort.js => sort.ts} (100%) rename src/composables/{localStorage.js => localStorage.ts} (100%) rename src/plugins/{vuetify.js => vuetify.ts} (89%) rename src/utils/{font-size.js => font-size.ts} (84%) rename src/utils/{index.js => index.ts} (74%) rename src/views/{views.js => views.ts} (89%) diff --git a/src/App.vue b/src/App.vue index 5aca6e51a..d481039a1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,7 +25,7 @@ along with this program. If not, see . - diff --git a/src/components/cylc/common/filter.js b/src/components/cylc/common/filter.ts similarity index 71% rename from src/components/cylc/common/filter.js rename to src/components/cylc/common/filter.ts index b39715cef..105d09b7c 100644 --- a/src/components/cylc/common/filter.js +++ b/src/components/cylc/common/filter.ts @@ -15,40 +15,35 @@ * along with this program. If not, see . */ +import { type Tokens } from '@/utils/uid' + /* Logic for filtering tasks. */ +interface Node { + node: { + state: string + } + tokens: Tokens +} + /** * Return true if the node ID matches the given ID, or if no ID is given. - * - * @param {Object} node - * @param {?string} id - * @return {boolean} */ -export function matchID (node, id) { - return !id?.trim() || node.tokens.relativeID.includes(id) +export function matchID (node: Node, id?: string): boolean { + return !id?.trim() || node.tokens.relativeID!.includes(id) } /** * Return true if the given list of states includes the node state, or if * if the list is empty. - * - * @param {Object} node - * @param {?string[]} states - * @returns {boolean} */ -export function matchState (node, states) { +export function matchState (node: Node, states?: string[]): boolean { return !states?.length || states.includes(node.node.state) } /** * Return true if a node matches the specified id/state filter. - * - * @export - * @param {Object} node - * @param {?string} id - * @param {?string[]} states - * @return {boolean} */ -export function matchNode (node, id, states) { +export function matchNode (node: Node, id?: string, states?: string[]): boolean { return matchID(node, id) && matchState(node, states) } diff --git a/src/components/cylc/common/sort.ts b/src/components/cylc/common/sort.ts index a8a24a141..417e81b29 100644 --- a/src/components/cylc/common/sort.ts +++ b/src/components/cylc/common/sort.ts @@ -24,7 +24,7 @@ type Obj = string | Record */ type SortedIndexByComparator = (leftObject: Obj, leftValue: string, rightObject: Obj, rightValue: string) => number -type SortedIndexOptions = { +interface SortedIndexOptions { comparator?: SortedIndexByComparator reverse?: boolean } @@ -45,11 +45,6 @@ export const DEFAULT_COMPARATOR = (left: string, right: string): number => { ) } -/** - * Declare function used in sortedIndexBy for creating the iteratee. - */ -type SortedIndexByIteratee = (value: string | Record) => string - /** * Given a list of elements, and a value to be added to the list, we * perform a simple binary search of the list to determine the next @@ -69,12 +64,19 @@ type SortedIndexByIteratee = (value: string | Record) => string * @param comparator - function used to compare the newValue with otherValues in the list * @return sorted index */ -export function sortedIndexBy (array: Obj[], value: Obj, iteratee: SortedIndexByIteratee, options: SortedIndexOptions = {}): number { +export function sortedIndexBy ( + array: Obj[], + value: Obj, + iteratee: (value: Obj) => string, + options: SortedIndexOptions = {} +): number { if (array.length === 0) { return 0 } // If given a function, use it. Otherwise, simply use locale sort with numeric enabled - const comparatorFunction = options.comparator || ((leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue)) + const comparatorFunction: SortedIndexByComparator = options.comparator || ( + (leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue) + ) let low = 0 let high = array.length diff --git a/src/components/cylc/gscan/GScan.vue b/src/components/cylc/gscan/GScan.vue index 6951544c4..8f0b041ef 100644 --- a/src/components/cylc/gscan/GScan.vue +++ b/src/components/cylc/gscan/GScan.vue @@ -121,7 +121,7 @@ import { TaskStateNames } from '@/model/TaskState.model' import { WorkflowState } from '@/model/WorkflowState.model' import Tree from '@/components/cylc/tree/Tree.vue' import { filterByName, filterByState } from '@/components/cylc/gscan/filters' -import { sortedWorkflowTree } from '@/components/cylc/gscan/sort.js' +import { sortedWorkflowTree } from '@/components/cylc/gscan/sort' import { mutate } from '@/utils/aotf' import TaskFilterSelect from '@/components/cylc/TaskFilterSelect.vue' diff --git a/src/components/cylc/gscan/filters.js b/src/components/cylc/gscan/filters.ts similarity index 68% rename from src/components/cylc/gscan/filters.js rename to src/components/cylc/gscan/filters.ts index ba5abff46..9e0c64486 100644 --- a/src/components/cylc/gscan/filters.js +++ b/src/components/cylc/gscan/filters.ts @@ -15,33 +15,35 @@ * along with this program. If not, see . */ -/** - * @param {WorkflowGScanNode|WorkflowNamePartGScanNode} workflow - * @param {?string} name - name filter - * @returns {boolean} - */ -export function filterByName (workflow, name) { +import { type Tokens } from '@/utils/uid' + +interface WorkflowNode { + node: { + status: string + stateTotals: Record + } + tokens: Tokens +} + +export function filterByName (workflow: WorkflowNode, name: string): boolean { return !name || workflow.tokens.workflow.toLowerCase().includes(name.toLowerCase()) } /** * @private - * @param {Object=} stateTotals - object with the keys being states, and values the count - * @return {string[]} + * @param stateTotals - object with the keys being states, and values the count */ -function getWorkflowStates (stateTotals) { +function getWorkflowStates (stateTotals: Record): string[] { return !stateTotals ? [] : Object.keys(stateTotals).filter((state) => stateTotals[state] > 0) } -/** - * @param {WorkflowGScanNode|WorkflowNamePartGScanNode} workflow - * @param {string[]} workflowStates - * @param {string[]} taskStates - * @returns {boolean} - */ -export function filterByState (workflow, workflowStates, taskStates) { +export function filterByState ( + workflow: WorkflowNode, + workflowStates: string[], + taskStates: string[] +): boolean { // workflow states if ( workflowStates.length && !workflowStates.includes(workflow.node.status) diff --git a/src/components/cylc/gscan/sort.js b/src/components/cylc/gscan/sort.ts similarity index 100% rename from src/components/cylc/gscan/sort.js rename to src/components/cylc/gscan/sort.ts diff --git a/src/components/cylc/workflow/Lumino.vue b/src/components/cylc/workflow/Lumino.vue index 61cea3d87..c14a29f14 100644 --- a/src/components/cylc/workflow/Lumino.vue +++ b/src/components/cylc/workflow/Lumino.vue @@ -76,7 +76,7 @@ const props = defineProps({ /** * All possible view component classes that can be rendered * - * @type {Map} + * @type {Map} */ allViews: { type: Map, diff --git a/src/components/cylc/workflow/Toolbar.vue b/src/components/cylc/workflow/Toolbar.vue index 68314aa70..e8f733ddb 100644 --- a/src/components/cylc/workflow/Toolbar.vue +++ b/src/components/cylc/workflow/Toolbar.vue @@ -192,7 +192,7 @@ export default { /** * All possible view component classes that can be rendered * - * @type {Map} + * @type {Map} */ views: { type: Map, diff --git a/src/composables/localStorage.js b/src/composables/localStorage.ts similarity index 100% rename from src/composables/localStorage.js rename to src/composables/localStorage.ts diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.ts similarity index 89% rename from src/plugins/vuetify.js rename to src/plugins/vuetify.ts index 09ffeb662..15972de9c 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.ts @@ -21,10 +21,14 @@ import { VCombobox } from 'vuetify/components/VCombobox' import { VSelect } from 'vuetify/components/VSelect' import { VTextarea } from 'vuetify/components/VTextarea' import { VTextField } from 'vuetify/components/VTextField' -import colors from 'vuetify/lib/util/colors' +import colors from 'vuetify/util/colors' import { mdiClose } from '@mdi/js' +import { + type VuetifyOptions, + type DefaultsInstance, +} from 'vuetify' -const inputDefaults = Object.fromEntries([ +const inputDefaults: DefaultsInstance = Object.fromEntries([ VAutocomplete, VCombobox, VSelect, @@ -40,10 +44,7 @@ const inputDefaults = Object.fromEntries([ } ])) -/** - * @type {import('vuetify').VuetifyOptions} - */ -export const vuetifyOptions = { +export const vuetifyOptions: VuetifyOptions = { theme: { defaultTheme: 'light', themes: { diff --git a/src/utils/font-size.js b/src/utils/font-size.ts similarity index 84% rename from src/utils/font-size.js rename to src/utils/font-size.ts index 47f597536..9c96882d7 100644 --- a/src/utils/font-size.js +++ b/src/utils/font-size.ts @@ -23,10 +23,10 @@ export const INCREMENT = 2 /** * Sets the font-size to a value. * - * @param {?string} size - Value with units given (doesn't matter which unit). - * If null then reset to default. + * @param size - Value with units given (doesn't matter which unit). + * If empty string then reset to default. */ -export function resetFontSize (size = null) { +export function resetFontSize (size: string = '') { localStorage.fontSize = size document.documentElement.style.fontSize = size } @@ -42,9 +42,9 @@ export function increaseFontSize () { /** * Get HTML element (computed) font size. * - * @returns {number} current font size in px + * @returns current font size in px */ -export function getCurrentFontSize () { +export function getCurrentFontSize (): number { const fontSize = window.getComputedStyle(document.documentElement).fontSize // px return parseFloat(fontSize) } diff --git a/src/utils/index.js b/src/utils/index.ts similarity index 74% rename from src/utils/index.js rename to src/utils/index.ts index 525e5d43f..77b162858 100644 --- a/src/utils/index.js +++ b/src/utils/index.ts @@ -15,17 +15,20 @@ * along with this program. If not, see . */ -import { watch } from 'vue' +import { + watch, + type WatchOptions, + type WatchSource, +} from 'vue' import { i18n } from '@/i18n' /** * i18n-enabled operation, to get the title respecting the locale used * in the application settings. - * @param {string} key - i18n key - * @param {Object} params - optional object key=value used in the i18n message - * @returns {string} + * @param key - i18n key + * @param params - optional object key=value used in the i18n message */ -export const getPageTitle = (key, params = {}) => { +export function getPageTitle (key: string, params = {}): string { return `${i18n.global.t('App.name')} | ${i18n.global.t(key, params)}` } @@ -33,12 +36,12 @@ export const getPageTitle = (key, params = {}) => { * Watch source until it is truthy, then call the callback (and stop watching). * * Immediate by default. - * - * @param {import('vue').WatchSource} source - * @param {import('vue').WatchCallback} callback - * @param {import('vue').WatchOptions?} options */ -export const when = (source, callback, options = {}) => { +export function when ( + source: WatchSource, + callback: () => void, + options: WatchOptions = {} +): void { const unwatch = watch( source, (ready) => { diff --git a/src/views/UserProfile.vue b/src/views/UserProfile.vue index 83f6e92dd..3d6d87953 100644 --- a/src/views/UserProfile.vue +++ b/src/views/UserProfile.vue @@ -246,7 +246,7 @@ import { mdiCog, mdiFormatFontSizeDecrease, mdiFormatFontSizeIncrease } from '@m import { useCyclePointsOrderDesc, useJobTheme, useReducedAnimation } from '@/composables/localStorage' import { getPageTitle } from '@/utils/index' import { decreaseFontSize, getCurrentFontSize, increaseFontSize, resetFontSize } from '@/utils/font-size' -import { allViews, useDefaultView } from '@/views/views.js' +import { allViews, useDefaultView } from '@/views/views' import Job from '@/components/cylc/Job.vue' import JobState from '@/model/JobState.model' diff --git a/src/views/Workspace.vue b/src/views/Workspace.vue index c8dfb9c3b..b2afc17c0 100644 --- a/src/views/Workspace.vue +++ b/src/views/Workspace.vue @@ -38,7 +38,7 @@ along with this program. If not, see . diff --git a/src/views/UserProfile.vue b/src/views/UserProfile.vue index 3d6d87953..f9f80dd7f 100644 --- a/src/views/UserProfile.vue +++ b/src/views/UserProfile.vue @@ -248,7 +248,7 @@ import { getPageTitle } from '@/utils/index' import { decreaseFontSize, getCurrentFontSize, increaseFontSize, resetFontSize } from '@/utils/font-size' import { allViews, useDefaultView } from '@/views/views' import Job from '@/components/cylc/Job.vue' -import JobState from '@/model/JobState.model' +import { JobStateNames } from '@/model/JobState.model' // TODO: update where user preferences are stored after #335 @@ -293,7 +293,7 @@ export default { }, }, - jobStates: JobState.enumValues.map(state => state.name), + jobStates: JobStateNames, jobThemes: [ 'default', diff --git a/src/views/Workspace.vue b/src/views/Workspace.vue index b2afc17c0..1d9220710 100644 --- a/src/views/Workspace.vue +++ b/src/views/Workspace.vue @@ -42,7 +42,7 @@ import { allViews } from '@/views/views' import { getPageTitle } from '@/utils/index' import graphqlMixin from '@/mixins/graphql' import subscriptionMixin from '@/mixins/subscription' -import ViewState from '@/model/ViewState.model' +import { ViewState } from '@/model/ViewState.model' import Lumino from '@/components/cylc/workflow/Lumino.vue' import Toolbar from '@/components/cylc/workflow/Toolbar.vue' import { toolbarHeight } from '@/utils/toolbar' diff --git a/tests/e2e/specs/dashboard.cy.js b/tests/e2e/specs/dashboard.cy.js index a7e0e568a..a5afe5d12 100644 --- a/tests/e2e/specs/dashboard.cy.js +++ b/tests/e2e/specs/dashboard.cy.js @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import { WorkflowStateOrder } from '@/model/WorkflowState.model' +import { WorkflowState } from '@/model/WorkflowState.model' describe('Dashboard', () => { beforeEach(() => { @@ -45,7 +45,7 @@ describe('Dashboard', () => { .then($tdElement => { return $tdElement[1].textContent.toLowerCase() }) - .should('equal', [...WorkflowStateOrder.entries()][0][0]) + .should('equal', Object.values(WorkflowState)[0]) }) it('Disables cylc hub button in single user mode', () => { diff --git a/tests/e2e/specs/table.cy.js b/tests/e2e/specs/table.cy.js index 40afeaae9..03d9fcd92 100644 --- a/tests/e2e/specs/table.cy.js +++ b/tests/e2e/specs/table.cy.js @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import TaskState from '@/model/TaskState.model' +import { TaskState } from '@/model/TaskState.model' const initialNumRows = 7 @@ -58,14 +58,14 @@ describe('Table view', () => { .should('have.length', initialNumRows) cy .get('td > div.d-flex > div') - .contains(TaskState.FAILED.name) + .contains(TaskState.FAILED) .should('be.visible') cy .get('[data-cy="filter task state"]') .click() cy .get('.v-list-item') - .contains(TaskState.RUNNING.name) + .contains(TaskState.RUNNING) .click({ force: true }) cy .get('td > div.d-flex > div') @@ -85,7 +85,7 @@ describe('Table view', () => { .click() cy .get('.v-list-item') - .contains(TaskState.SUCCEEDED.name) + .contains(TaskState.SUCCEEDED) .click({ force: true }) cy .get('.c-table table > tbody > tr') diff --git a/tests/e2e/specs/tree.cy.js b/tests/e2e/specs/tree.cy.js index 99174a096..6ad018edb 100644 --- a/tests/e2e/specs/tree.cy.js +++ b/tests/e2e/specs/tree.cy.js @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import TaskState from '@/model/TaskState.model' +import { TaskState, TaskStateNames } from '@/model/TaskState.model' describe('Tree view', () => { it('Should display cycle points for the mocked workflow', () => { @@ -217,7 +217,7 @@ describe('Tree view', () => { cy.get('[data-cy="filter task state"]') .click() .get('.v-list-item') - .contains(new RegExp(`^${TaskState.FAILED.name}$`)) + .contains(new RegExp(`^${TaskState.FAILED}$`)) .click() for (const name of [/^succeeded$/, /^retrying$/]) { cy.get('.node-data-task') @@ -243,7 +243,7 @@ describe('Tree view', () => { .get('[data-cy="filter task state"]') .click() .get('.v-list-item') - .contains(TaskState.WAITING.name) + .contains(TaskState.WAITING) .click({ force: true }) cy .get('.node-data-task:visible') @@ -343,12 +343,11 @@ describe('Tree view', () => { cy.visit('/#/tree/one') cy.get('[data-cy="filter task state"]') .click() - // eslint-disable-next-line no-lone-blocks - TaskState.enumValues.forEach(state => { + for (const state of TaskStateNames) { cy.get('.v-list-item') - .contains(state.name) + .contains(state) .click({ force: true }) - }) + } // Click outside to close dropdown cy.get('noscript') .click({ force: true }) diff --git a/tests/unit/components/cylc/gscan/gscan.vue.spec.js b/tests/unit/components/cylc/gscan/gscan.vue.spec.js index 9fc9fde55..2af0eeead 100644 --- a/tests/unit/components/cylc/gscan/gscan.vue.spec.js +++ b/tests/unit/components/cylc/gscan/gscan.vue.spec.js @@ -23,9 +23,9 @@ import GScan from '@/components/cylc/gscan/GScan.vue' import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin' import { WorkflowState, - WorkflowStateOrder + getWorkflowStateOrder } from '@/model/WorkflowState.model' -import TaskState from '@/model/TaskState.model' +import { TaskState } from '@/model/TaskState.model' import { getWorkflowTreeSortValue, sortedWorkflowTree @@ -58,15 +58,15 @@ describe('GScan component', () => { describe('Sorting', () => { it('sets workflow sort order by status', () => { // for each worflow state ... - for (const workflowState of WorkflowState) { + for (const workflowState of Object.values(WorkflowState)) { // it should associate a workflow with the correct sort order expect( getWorkflowTreeSortValue({ type: 'workflow', - node: { status: workflowState.name }, + node: { status: workflowState }, children: [] }) - ).to.equal(WorkflowStateOrder.get(workflowState.name)) + ).to.equal(getWorkflowStateOrder(workflowState)) // it should associate a nested workflow with the correct sort order expect( @@ -76,7 +76,7 @@ describe('GScan component', () => { children: [ { type: 'workflow', - node: { status: workflowState.name }, + node: { status: workflowState }, children: [] }, { @@ -87,7 +87,7 @@ describe('GScan component', () => { } ] }) - ).to.equal(WorkflowStateOrder.get(workflowState.name)) + ).to.equal(getWorkflowStateOrder(workflowState)) } }) @@ -135,7 +135,7 @@ describe('GScan component', () => { const filteredOutNodesCache = new Map() // filter for all workflow states await wrapper.setData({ - filters: { 'workflow state': WorkflowStateOrder.keys() }, + filters: { 'workflow state': Object.values(WorkflowState) }, }) filterNodes(wrapper, filteredOutNodesCache) expect(getIDMap(filteredOutNodesCache)).toEqual({ @@ -152,7 +152,7 @@ describe('GScan component', () => { const filteredOutNodesCache = new Map() await wrapper.setData({ - filters: { 'workflow state': [WorkflowState.RUNNING.name] }, + filters: { 'workflow state': [WorkflowState.RUNNING] }, }) filterNodes(wrapper, filteredOutNodesCache) expect(getIDMap(filteredOutNodesCache)).toEqual({ @@ -166,8 +166,8 @@ describe('GScan component', () => { await wrapper.setData({ filters: { 'workflow state': [ - WorkflowState.STOPPING.name, - WorkflowState.STOPPED.name, + WorkflowState.STOPPING, + WorkflowState.STOPPED, ] }, }) @@ -211,7 +211,7 @@ describe('GScan component', () => { const filteredOutNodesCache = new Map() await wrapper.setData({ - filters: { 'task state': [TaskState.RUNNING.name] } + filters: { 'task state': [TaskState.RUNNING] } }) filterNodes(wrapper, filteredOutNodesCache) expect(getIDMap(filteredOutNodesCache)).toEqual({ @@ -223,7 +223,7 @@ describe('GScan component', () => { }) await wrapper.setData({ - filters: { 'task state': [TaskState.SUBMITTED.name] } + filters: { 'task state': [TaskState.SUBMITTED] } }) filterNodes(wrapper, filteredOutNodesCache) expect(getIDMap(filteredOutNodesCache)).toEqual({ @@ -242,8 +242,8 @@ describe('GScan component', () => { await wrapper.setData({ searchWorkflows: 'a', filters: { - 'workflow state': [WorkflowState.STOPPED.name], - 'task state': [TaskState.SUBMIT_FAILED.name], + 'workflow state': [WorkflowState.STOPPED], + 'task state': [TaskState.SUBMIT_FAILED], }, }) filterNodes(wrapper, filteredOutNodesCache) diff --git a/tests/unit/components/cylc/gscan/utils.js b/tests/unit/components/cylc/gscan/utils.js index 96765575b..8ab80e0d2 100644 --- a/tests/unit/components/cylc/gscan/utils.js +++ b/tests/unit/components/cylc/gscan/utils.js @@ -16,21 +16,21 @@ */ import { WorkflowState } from '@/model/WorkflowState.model' -import TaskState from '@/model/TaskState.model' +import { TaskState } from '@/model/TaskState.model' import { Tokens } from '@/utils/uid' const RUNNING_STATE_TOTALS = { - [TaskState.RUNNING.name]: 1, - [TaskState.SUBMITTED.name]: 0, + [TaskState.RUNNING]: 1, + [TaskState.SUBMITTED]: 0, } const SUBMITTED_STATE_TOTALS = { - [TaskState.RUNNING.name]: 0, - [TaskState.SUBMITTED.name]: 1, + [TaskState.RUNNING]: 0, + [TaskState.SUBMITTED]: 1, } const SUBMIT_FAILED_STATE_TOTALS = { - [TaskState.SUBMIT_FAILED.name]: 1, + [TaskState.SUBMIT_FAILED]: 1, } export const TEST_TREE = { @@ -52,7 +52,7 @@ export const TEST_TREE = { type: 'workflow', tokens: new Tokens('~u/a/x1'), node: { - status: WorkflowState.STOPPED.name, + status: WorkflowState.STOPPED, stateTotals: SUBMIT_FAILED_STATE_TOTALS, latestStateTasks: [], } @@ -63,7 +63,7 @@ export const TEST_TREE = { type: 'workflow', tokens: new Tokens('~u/a/x2'), node: { - status: WorkflowState.STOPPED.name, + status: WorkflowState.STOPPED, latestStateTasks: [], } } @@ -75,7 +75,7 @@ export const TEST_TREE = { type: 'workflow', tokens: new Tokens('~u/b'), node: { - status: WorkflowState.STOPPING.name, + status: WorkflowState.STOPPING, stateTotals: RUNNING_STATE_TOTALS, latestStateTasks: [], } @@ -86,7 +86,7 @@ export const TEST_TREE = { type: 'workflow', tokens: new Tokens('~u/c'), node: { - status: WorkflowState.RUNNING.name, + status: WorkflowState.RUNNING, stateTotals: SUBMITTED_STATE_TOTALS, latestStateTasks: [], } diff --git a/tests/unit/components/cylc/gscan/workflowicon.spec.js b/tests/unit/components/cylc/gscan/workflowicon.spec.js index be4226129..922d50abd 100644 --- a/tests/unit/components/cylc/gscan/workflowicon.spec.js +++ b/tests/unit/components/cylc/gscan/workflowicon.spec.js @@ -17,7 +17,7 @@ import { shallowMount } from '@vue/test-utils' import WorkflowIcon from '@/components/cylc/gscan/WorkflowIcon.vue' -import WorkflowState from '@/model/WorkflowState.model' +import { WorkflowState, WorkflowStateIcons } from '@/model/WorkflowState.model' import { createVuetify } from 'vuetify' import { mdiHelpCircle } from '@mdi/js' @@ -30,8 +30,8 @@ describe('WorkflowIcon', () => { expected: mdiHelpCircle }, { - status: WorkflowState.STOPPED.name, - expected: WorkflowState.STOPPED.icon + status: WorkflowState.STOPPED, + expected: WorkflowStateIcons.get(WorkflowState.STOPPED) }, ])('uses the right icon for state: $status', ({ status, expected }) => { const wrapper = shallowMount(WorkflowIcon, { diff --git a/tests/unit/components/cylc/table/table.data.js b/tests/unit/components/cylc/table/table.data.js index f51dc8276..14b519083 100644 --- a/tests/unit/components/cylc/table/table.data.js +++ b/tests/unit/components/cylc/table/table.data.js @@ -14,8 +14,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import TaskState from '@/model/TaskState.model' -import JobState from '@/model/JobState.model' +import { TaskState } from '@/model/TaskState.model' +import { JobState } from '@/model/JobState.model' import { Tokens } from '@/utils/uid' const tk = { @@ -32,7 +32,7 @@ export const simpleTableTasks = [ tokens: tk.taskA, node: { id: tk.taskA.id, - state: TaskState.RUNNING.name, + state: TaskState.RUNNING, task: { meanElapsedTime: 2, }, @@ -49,7 +49,7 @@ export const simpleTableTasks = [ submittedTime: new Date().toISOString(), startedTime: new Date().toISOString(), finishedTime: null, - state: JobState.RUNNING.name + state: JobState.RUNNING }, children: [] } @@ -66,7 +66,7 @@ export const simpleTableTasks = [ submittedTime: new Date().toISOString(), startedTime: new Date().toISOString(), finishedTime: null, - state: JobState.RUNNING.name + state: JobState.RUNNING }, children: [] }, @@ -79,7 +79,7 @@ export const simpleTableTasks = [ tokens: tk.taskB, node: { id: tk.taskB.id, - state: TaskState.WAITING.name, + state: TaskState.WAITING, name: 'taskB', }, children: [] @@ -94,7 +94,7 @@ export const simpleTableTasks = [ tokens: tk.taskC, node: { id: tk.taskC.id, - state: TaskState.SUBMITTED.name, + state: TaskState.SUBMITTED, name: 'taskC', }, children: [] diff --git a/tests/unit/components/cylc/table/table.vue.spec.js b/tests/unit/components/cylc/table/table.vue.spec.js index 9d9fd41ed..5ecc7001c 100644 --- a/tests/unit/components/cylc/table/table.vue.spec.js +++ b/tests/unit/components/cylc/table/table.vue.spec.js @@ -19,7 +19,7 @@ import { mount } from '@vue/test-utils' import { createVuetify } from 'vuetify' import sinon from 'sinon' import { simpleTableTasks } from './table.data' -import TaskState from '@/model/TaskState.model' +import { TaskState } from '@/model/TaskState.model' import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin' import Table from '@/components/cylc/table/Table.vue' import WorkflowService from '@/services/workflow.service' @@ -106,7 +106,7 @@ describe('Table component', () => { wrapper.vm.tasksFilter = { id: '', states: [ - TaskState.WAITING.name + TaskState.WAITING ] } // await nextTick() @@ -122,7 +122,7 @@ describe('Table component', () => { wrapper.vm.tasksFilter = { id: 'taskA', states: [ - TaskState.WAITING.name + TaskState.WAITING ] } expect(wrapper.vm.filteredTasks.length).to.equal(0) diff --git a/tests/unit/components/cylc/toolbar.vue.spec.js b/tests/unit/components/cylc/toolbar.vue.spec.js index ccefc99df..0f7120497 100644 --- a/tests/unit/components/cylc/toolbar.vue.spec.js +++ b/tests/unit/components/cylc/toolbar.vue.spec.js @@ -19,7 +19,7 @@ import { mount } from '@vue/test-utils' import { createVuetify } from 'vuetify' import { createStore } from 'vuex' import Toolbar from '@/components/cylc/Toolbar.vue' -import WorkflowState from '@/model/WorkflowState.model' +import { WorkflowState } from '@/model/WorkflowState.model' import storeOptions from '@/store/options' describe('Toolbar component', () => { @@ -45,7 +45,7 @@ describe('Toolbar component', () => { { id: 'user/id', name: 'test', - status: WorkflowState.RUNNING.name + status: WorkflowState.RUNNING } ] store.state.workflows.workflowName = 'test' diff --git a/tests/unit/mixins/subscription.spec.js b/tests/unit/mixins/subscription.spec.js index 51c79076d..725536bf6 100644 --- a/tests/unit/mixins/subscription.spec.js +++ b/tests/unit/mixins/subscription.spec.js @@ -20,7 +20,7 @@ import sinon from 'sinon' import { toRaw } from 'vue' import { createStore } from 'vuex' import { Alert } from '@/model/Alert.model' -import ViewState from '@/model/ViewState.model' +import { ViewState } from '@/model/ViewState.model' import storeOptions from '@/store/options' import subscriptionMixin from '@/mixins/subscription' diff --git a/tests/unit/model/subscription.model.spec.js b/tests/unit/model/subscription.model.spec.js index 79c8b7669..98c0be747 100644 --- a/tests/unit/model/subscription.model.spec.js +++ b/tests/unit/model/subscription.model.spec.js @@ -19,7 +19,7 @@ import sinon from 'sinon' import gql from 'graphql-tag' import Subscription from '@/model/Subscription.model' import SubscriptionQuery from '@/model/SubscriptionQuery.model' -import ViewState from '@/model/ViewState.model' +import { ViewState } from '@/model/ViewState.model' describe('SubscriptionQuery model', () => { const query = gql`query { workflow { id } }` diff --git a/tests/unit/model/taskstate.model.spec.js b/tests/unit/model/taskstate.model.spec.js index 491dc8ef1..0c6ec6711 100644 --- a/tests/unit/model/taskstate.model.spec.js +++ b/tests/unit/model/taskstate.model.spec.js @@ -23,7 +23,7 @@ import { describe('TaskState model', () => { describe('TaskStateUserOrder', () => { it('contains the same states as TaskState in a different order', () => { - expect([...TaskState].sort()).to.deep.equal(TaskStateUserOrder.sort()) + expect(Object.values(TaskState).sort()).to.deep.equal(TaskStateUserOrder.sort()) }) }) }) diff --git a/tests/unit/services/workflow.service.spec.js b/tests/unit/services/workflow.service.spec.js index 3ae3bbff3..6044f89f4 100644 --- a/tests/unit/services/workflow.service.spec.js +++ b/tests/unit/services/workflow.service.spec.js @@ -27,7 +27,7 @@ import Subscription from '@/model/Subscription.model' import SubscriptionQuery from '@/model/SubscriptionQuery.model' import WorkflowService from '@/services/workflow.service' import * as graphqlModule from '@/graphql/index' -import ViewState from '@/model/ViewState.model' +import { ViewState } from '@/model/ViewState.model' import { TreeCallback, WorkflowCallback } from './testCallback' const sandbox = sinon.createSandbox() diff --git a/tests/unit/utils/tasks.spec.js b/tests/unit/utils/tasks.spec.js index 0511e3f36..98fc8676a 100644 --- a/tests/unit/utils/tasks.spec.js +++ b/tests/unit/utils/tasks.spec.js @@ -15,48 +15,53 @@ * along with this program. If not, see . */ -import TaskState from '@/model/TaskState.model' +import { TaskState } from '@/model/TaskState.model' import { dtMean, extractGroupState, latestJob, formatDuration, jobMessageOutputs } from '@/utils/tasks' describe('tasks', () => { describe('extractGroupState', () => { - it('should return the correct state for the node groups when not stopped', () => { - [ - [ - TaskState.FAILED.name, // expected - [TaskState.WAITING, TaskState.FAILED].map((state) => state.name)], // childStates - [ - TaskState.WAITING.name, - [TaskState.WAITING].map((state) => state.name)], - [ - TaskState.RUNNING.name, - [TaskState.SUBMITTED, TaskState.RUNNING].map((state) => state.name)] - ].forEach((val) => { - const groupState = extractGroupState(val[1], false) - expect(groupState).to.equal(val[0]) - }) - }) - - it('should return the correct state for the node groups when stopped', () => { - [ - [ - TaskState.RUNNING.name, // expected - [TaskState.WAITING, TaskState.SUBMITTED, TaskState.RUNNING].map((state) => state.name)], // childStates - [ - TaskState.SUCCEEDED.name, - [TaskState.SUCCEEDED].map((state) => state.name)], - [ - TaskState.RUNNING.name, - [TaskState.SUBMITTED, TaskState.RUNNING, TaskState.EXPIRED].map((state) => state.name)] - ].forEach((val) => { - const groupState = extractGroupState(val[1], true) - expect(groupState).to.equal(val[0]) - }) - }) - - it('should return empty when no states provided', () => { - expect(extractGroupState([])).to.equal('') - }) + it.each([ + { + childStates: [TaskState.WAITING, TaskState.FAILED], + stopped: false, + expected: TaskState.FAILED + }, + { + childStates: [TaskState.WAITING], + stopped: false, + expected: TaskState.WAITING + }, + { + childStates: [TaskState.SUBMITTED, TaskState.RUNNING], + stopped: false, + expected: TaskState.RUNNING + }, + { + childStates: [TaskState.WAITING, TaskState.SUBMITTED, TaskState.RUNNING], + stopped: true, + expected: TaskState.RUNNING + }, + { + childStates: [TaskState.SUCCEEDED], + stopped: true, + expected: TaskState.SUCCEEDED + }, + { + childStates: [TaskState.SUBMITTED, TaskState.RUNNING, TaskState.EXPIRED], + stopped: true, + expected: TaskState.RUNNING + }, + { + childStates: [], + expected: '' + } + ])( + 'extractGroupState($childStates, stopped = $stopped) -> $expected', + ({ childStates, stopped, expected }) => { + const groupState = extractGroupState(childStates, stopped) + expect(groupState).to.equal(expected) + } + ) }) describe.each([ diff --git a/yarn.lock b/yarn.lock index 3f071e209..29075c53d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3805,7 +3805,6 @@ __metadata: cypress: "npm:13.7.0" cypress-vite: "npm:1.5.0" dedent: "npm:1.5.1" - enumify: "npm:2.0.0" eslint: "npm:8.57.0" eslint-config-standard: "npm:17.1.0" eslint-plugin-compat: "npm:4.2.0" @@ -4254,13 +4253,6 @@ __metadata: languageName: node linkType: hard -"enumify@npm:2.0.0": - version: 2.0.0 - resolution: "enumify@npm:2.0.0" - checksum: 10c0/641096d9614fa33738ace4aa3cbc17bb38a9a8ea906bfed3214e76e084c9f20f75f898fc4488ab8bbb0425800b629c7ccc6e646f2a05c51fb86d3636f06df291 - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" From 879f71eba188247cff61f0136ba0e4859b0e9bd9 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:33:21 +0000 Subject: [PATCH 13/14] fixup! fixup! Enable TypeScript --- .eslintrc.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6936bccc9..854ae69ad 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -75,6 +75,7 @@ module.exports = { 'error', { functions: false, + variables: false, } ], 'vue/multi-word-component-names': [ From c2a821d84cfa096d3cd951fa687b977b630d473a Mon Sep 17 00:00:00 2001 From: Mark Dawson Date: Fri, 5 Apr 2024 13:38:16 +0100 Subject: [PATCH 14/14] made header language typescript --- src/components/cylc/Header.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cylc/Header.vue b/src/components/cylc/Header.vue index 936e5ad66..9e9a153a9 100644 --- a/src/components/cylc/Header.vue +++ b/src/components/cylc/Header.vue @@ -117,7 +117,7 @@ along with this program. If not, see . -