mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-21 18:41:40 +03:00
UBERF-8520: Test management (#7154)
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
parent
8e511653cc
commit
79659c4c32
@ -545,6 +545,9 @@ dependencies:
|
||||
'@rush-temp/model-templates':
|
||||
specifier: file:./projects/model-templates.tgz
|
||||
version: file:projects/model-templates.tgz
|
||||
'@rush-temp/model-test-management':
|
||||
specifier: file:./projects/model-test-management.tgz
|
||||
version: file:projects/model-test-management.tgz
|
||||
'@rush-temp/model-text-editor':
|
||||
specifier: file:./projects/model-text-editor.tgz
|
||||
version: file:projects/model-text-editor.tgz
|
||||
@ -1022,6 +1025,15 @@ dependencies:
|
||||
'@rush-temp/templates-resources':
|
||||
specifier: file:./projects/templates-resources.tgz
|
||||
version: file:projects/templates-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
|
||||
'@rush-temp/test-management':
|
||||
specifier: file:./projects/test-management.tgz
|
||||
version: file:projects/test-management.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2)
|
||||
'@rush-temp/test-management-assets':
|
||||
specifier: file:./projects/test-management-assets.tgz
|
||||
version: file:projects/test-management-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2)
|
||||
'@rush-temp/test-management-resources':
|
||||
specifier: file:./projects/test-management-resources.tgz
|
||||
version: file:projects/test-management-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
|
||||
'@rush-temp/tests-sanity':
|
||||
specifier: file:./projects/tests-sanity.tgz
|
||||
version: file:projects/tests-sanity.tgz
|
||||
@ -22727,7 +22739,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/desktop.tgz(bufferutil@4.0.8)(sass@1.71.1)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-VjzxhhavCElN2Ak6+7CforJ3IJK0/iTE6crFhcRZrbq4RajWlGEHpD/o7O3mun0xepqA0QxpHCkTIO6SQdPsOg==, tarball: file:projects/desktop.tgz}
|
||||
resolution: {integrity: sha512-rjywmlFY/JneloAn6/L8+zLke89G1qy9ROO+fB2hJwYZDK0JGaN0CE68Sq8iZX6fJ+sAIGW6IsbbfpsvhmhUag==, tarball: file:projects/desktop.tgz}
|
||||
id: file:projects/desktop.tgz
|
||||
name: '@rush-temp/desktop'
|
||||
version: 0.0.0
|
||||
@ -24471,7 +24483,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz:
|
||||
resolution: {integrity: sha512-lZSE4CGfPJX/cTsbqnSoz/ge496Cwl3zeRFHM2wrpgfCFyeCNp00xpRv62LPGe38dvHo/IIzy+Uu0RSVPHUOPw==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-Fxv8P/cfFWJfFli1C70fOej/quvHGGyzJBLpbEixsPV2F+pqX53mwpTzAPqVkHLFVwbRkpnociSpaJQYyx0Beg==, tarball: file:projects/model-all.tgz}
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -25719,6 +25731,24 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/model-test-management.tgz:
|
||||
resolution: {integrity: sha512-p0y76Msm6v7Zg8KlvMDiWV3BKCDEw8KjV9sUwC+wkMvKnJ4tOtErd+OdnJFKOyGbtX6yX8rCSp6NIJwqmMmhSA==, tarball: file:projects/model-test-management.tgz}
|
||||
name: '@rush-temp/model-test-management'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
prettier: 3.2.5
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/model-text-editor.tgz:
|
||||
resolution: {integrity: sha512-z1TlVHF8VxcMeChSZ70ZGJsKdGSKisnpnPGuLmp2yYS30ckD5ohoj4KeqgbKu+qT+72FYSg4COXFoAYnszgfoA==, tarball: file:projects/model-text-editor.tgz}
|
||||
name: '@rush-temp/model-text-editor'
|
||||
@ -27488,7 +27518,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-LpzXVVxqqH4eEF3U7B9uOy/zBFWAZ+FGeGqIBCo9N7UzWxndKf7LZx+eYJQ8gsg5aVoqlGAbXjZJHHSVERGeJQ==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-vNHiwvJN+OiPr+lWDdxC3+YB6vZPF0QHYXMXrmHcagQH5vEfVzRPmL9F8pTnx/MHPvAlDt4DqgQls77F8LFYgg==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -29382,7 +29412,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-indexer.tgz(esbuild@0.20.1)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-H/iX6BGnUy04J35NoefNnWyoCqEd5RFve5SMXioIuA4tWqglHcAz8JqyPPLlfN+onrsh4Pz0tTVRV6lOZXSXWw==, tarball: file:projects/server-indexer.tgz}
|
||||
resolution: {integrity: sha512-X0K8LN7D/inN8B9B+/ERJTAm1l0gQ7fA9nOdMMVUHEVZqmq6WoiSZ+vPxdY/dx+MQlYFTcPumxLgZiVCZET4Og==, tarball: file:projects/server-indexer.tgz}
|
||||
id: file:projects/server-indexer.tgz
|
||||
name: '@rush-temp/server-indexer'
|
||||
version: 0.0.0
|
||||
@ -31256,6 +31286,113 @@ packages:
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
file:projects/test-management-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-5goSfMWyruB2GJwVYnn8e9YYJW523dDrcME75Og3+yem8W+yUcteJ+Sy2qgMNjCzC6ADzxCM5TIHItnCpH0gjQ==, tarball: file:projects/test-management-assets.tgz}
|
||||
id: file:projects/test-management-assets.tgz
|
||||
name: '@rush-temp/test-management-assets'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
'@types/node': 20.11.19
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- esbuild
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
file:projects/test-management-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-o+axSz7dIMDinwB3h8Ntr1pP35PGsUtKtTUvEqJFQ1lA+KQdTJ9akLrijzp9T6xRSdTPa6WNG6yjtqM8yhC70w==, tarball: file:projects/test-management-resources.tgz}
|
||||
id: file:projects/test-management-resources.tgz
|
||||
name: '@rush-temp/test-management-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.19)(ts-node@10.9.2)
|
||||
fast-equals: 5.0.1
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
prettier: 3.2.5
|
||||
prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.19)
|
||||
sass: 1.71.1
|
||||
svelte: 4.2.19
|
||||
svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.19)
|
||||
svelte-eslint-parser: 0.33.1(svelte@4.2.19)
|
||||
svelte-loader: 3.2.0(svelte@4.2.19)
|
||||
svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.19)(typescript@5.6.2)
|
||||
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- '@types/node'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- coffeescript
|
||||
- esbuild
|
||||
- less
|
||||
- node-notifier
|
||||
- postcss
|
||||
- postcss-load-config
|
||||
- pug
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
file:projects/test-management.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-zLxjXJwufmBoWJS5xoCGxG9uybC/T7UtJ+g4PlSnDv16aGSPDbnybLtQ0uorYvlOESpcSgQGrVLCgOhIB90tfw==, tarball: file:projects/test-management.tgz}
|
||||
id: file:projects/test-management.tgz
|
||||
name: '@rush-temp/test-management'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
lexorank: 1.0.5
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- '@types/node'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- esbuild
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
file:projects/tests-sanity.tgz:
|
||||
resolution: {integrity: sha512-noV3nlBP0OvM6i4C5YUevkhltHxG/2bct4pusP3O3AEQ7Za+A4qwzpsE08pHfA9f9pedEG677GE4EmG9x61rVQ==, tarball: file:projects/tests-sanity.tgz}
|
||||
name: '@rush-temp/tests-sanity'
|
||||
@ -31748,7 +31885,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tracker-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-6wsyMyxPpL8OKwTKistPKvRxkmmptqXJwxFZ5VOOlGuEwqXFYIPYZNR6taZd33fq1Y97L+vKeP65rWmXhbRIdQ==, tarball: file:projects/tracker-resources.tgz}
|
||||
resolution: {integrity: sha512-k1Lw3hvnvA+K22hLtdZKz4+ZQbk451p8bQL0RIHZ/yS/dIBLlBpBUU279YAqH2zxlmlFt1+C1WsTsrrPRdfx7A==, tarball: file:projects/tracker-resources.tgz}
|
||||
id: file:projects/tracker-resources.tgz
|
||||
name: '@rush-temp/tracker-resources'
|
||||
version: 0.0.0
|
||||
|
@ -204,6 +204,9 @@
|
||||
"@hcengineering/analytics-collector-resources": "^0.6.0",
|
||||
"@hcengineering/ai-bot": "^0.6.0",
|
||||
"@hcengineering/ai-bot-resources": "^0.6.0",
|
||||
"@hcengineering/test-management": "^0.6.0",
|
||||
"@hcengineering/test-management-assets": "^0.6.0",
|
||||
"@hcengineering/test-management-resources": "^0.6.0",
|
||||
"electron-squirrel-startup": "~1.0.0",
|
||||
"dotenv": "~16.0.0",
|
||||
"electron-context-menu": "^4.0.4",
|
||||
|
@ -53,6 +53,7 @@ import { questionsId } from '@hcengineering/questions'
|
||||
import { trainingId } from '@hcengineering/training'
|
||||
import { documentsId } from '@hcengineering/controlled-documents'
|
||||
import aiBot, { aiBotId } from '@hcengineering/ai-bot'
|
||||
import { testManagementId } from '@hcengineering/test-management'
|
||||
|
||||
import '@hcengineering/activity-assets'
|
||||
import '@hcengineering/attachment-assets'
|
||||
@ -94,6 +95,7 @@ import '@hcengineering/products-assets'
|
||||
import '@hcengineering/controlled-documents-assets'
|
||||
import '@hcengineering/analytics-collector-assets'
|
||||
import '@hcengineering/text-editor-assets'
|
||||
import '@hcengineering/test-management-assets'
|
||||
|
||||
import { coreId } from '@hcengineering/core'
|
||||
import presentation, { parsePreviewConfig, parseUploadConfig, presentationId } from '@hcengineering/presentation'
|
||||
@ -185,6 +187,7 @@ function configureI18n (): void {
|
||||
addStringsLoader(loveId, async (lang: string) => await import(`@hcengineering/love-assets/lang/${lang}.json`))
|
||||
addStringsLoader(printId, async (lang: string) => await import(`@hcengineering/print-assets/lang/${lang}.json`))
|
||||
addStringsLoader(analyticsCollectorId, async (lang: string) => await import(`@hcengineering/analytics-collector-assets/lang/${lang}.json`))
|
||||
addStringsLoader(testManagementId, async (lang: string) => await import(`@hcengineering/test-management-assets/lang/${lang}.json`))
|
||||
}
|
||||
|
||||
export async function configurePlatform (): Promise<void> {
|
||||
@ -306,6 +309,7 @@ export async function configurePlatform (): Promise<void> {
|
||||
addLocation(loveId, () => import(/* webpackChunkName: "love" */ '@hcengineering/love-resources'))
|
||||
addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources'))
|
||||
addLocation(textEditorId, () => import(/* webpackChunkName: "text-editor" */ '@hcengineering/text-editor-resources'))
|
||||
addLocation(testManagementId, () => import(/* webpackChunkName: "test-management" */ '@hcengineering/test-management-resources'))
|
||||
|
||||
setMetadata(client.metadata.FilterModel, 'ui')
|
||||
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])
|
||||
|
@ -233,6 +233,9 @@
|
||||
"@hcengineering/products-resources": "^0.1.0",
|
||||
"@hcengineering/ai-bot": "^0.6.0",
|
||||
"@hcengineering/ai-bot-resources": "^0.6.0",
|
||||
"@hcengineering/test-management": "^0.6.0",
|
||||
"@hcengineering/test-management-assets": "^0.6.0",
|
||||
"@hcengineering/test-management-resources": "^0.6.0",
|
||||
"@sentry/svelte": "~7.101.0",
|
||||
"posthog-js": "~1.122.0"
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import textEditor, { textEditorId } from '@hcengineering/text-editor'
|
||||
import analyticsCollector, {analyticsCollectorId} from '@hcengineering/analytics-collector'
|
||||
import { uploaderId } from '@hcengineering/uploader'
|
||||
import aiBot, { aiBotId } from '@hcengineering/ai-bot'
|
||||
import { testManagementId } from '@hcengineering/test-management'
|
||||
|
||||
import { bitrixId } from '@hcengineering/bitrix'
|
||||
|
||||
@ -103,6 +104,7 @@ import '@hcengineering/controlled-documents-assets'
|
||||
import '@hcengineering/analytics-collector-assets'
|
||||
import '@hcengineering/text-editor-assets'
|
||||
import '@hcengineering/uploader-assets'
|
||||
import '@hcengineering/test-management-assets'
|
||||
|
||||
import github, { githubId } from '@hcengineering/github'
|
||||
import '@hcengineering/github-assets'
|
||||
@ -231,6 +233,7 @@ function configureI18n(): void {
|
||||
addStringsLoader(loveId, async (lang: string) => await import(`@hcengineering/love-assets/lang/${lang}.json`))
|
||||
addStringsLoader(printId, async (lang: string) => await import(`@hcengineering/print-assets/lang/${lang}.json`))
|
||||
addStringsLoader(analyticsCollectorId, async (lang: string) => await import(`@hcengineering/analytics-collector-assets/lang/${lang}.json`))
|
||||
addStringsLoader(testManagementId, async (lang: string) => await import(`@hcengineering/test-management-assets/lang/${lang}.json`))
|
||||
}
|
||||
|
||||
export async function configurePlatform() {
|
||||
@ -399,6 +402,7 @@ export async function configurePlatform() {
|
||||
addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources'))
|
||||
addLocation(textEditorId, () => import(/* webpackChunkName: "text-editor" */ '@hcengineering/text-editor-resources'))
|
||||
addLocation(uploaderId, () => import(/* webpackChunkName: "uploader" */ '@hcengineering/uploader-resources'))
|
||||
addLocation(testManagementId, () => import(/* webpackChunkName: "test-management" */ '@hcengineering/test-management-resources'))
|
||||
|
||||
setMetadata(client.metadata.FilterModel, 'ui')
|
||||
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])
|
||||
|
@ -110,6 +110,7 @@
|
||||
"@hcengineering/model-analytics-collector": "^0.6.0",
|
||||
"@hcengineering/model-server-ai-bot": "^0.6.0",
|
||||
"@hcengineering/model-ai-bot": "^0.6.0",
|
||||
"@hcengineering/model-server-fulltext": "^0.6.0"
|
||||
"@hcengineering/model-server-fulltext": "^0.6.0",
|
||||
"@hcengineering/model-test-management": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,11 @@ import documents, { documentsId, createModel as documentsModel } from '@hcengine
|
||||
import products, { productsId, createModel as productsModel } from '@hcengineering/model-products'
|
||||
import { serverProductsId, createModel as serverProductsModel } from '@hcengineering/model-server-products'
|
||||
import { serverTrainingId, createModel as serverTrainingModel } from '@hcengineering/model-server-training'
|
||||
import testManagement, {
|
||||
testManagementId,
|
||||
createModel as testManagementModel
|
||||
} from '@hcengineering/model-test-management'
|
||||
|
||||
import {
|
||||
serverDocumentsId,
|
||||
createModel as serverDocumentsModel
|
||||
@ -404,6 +409,17 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
[
|
||||
testManagementModel,
|
||||
testManagementId,
|
||||
{
|
||||
label: testManagement.string.ConfigLabel,
|
||||
description: testManagement.string.ConfigDescription,
|
||||
enabled: false,
|
||||
beta: false,
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
|
||||
[serverCoreModel, serverCoreId],
|
||||
[serverAttachmentModel, serverAttachmentId],
|
||||
|
@ -52,6 +52,7 @@ import { productsOperation } from '@hcengineering/model-products'
|
||||
import { requestOperation } from '@hcengineering/model-request'
|
||||
import { analyticsCollectorOperation } from '@hcengineering/model-analytics-collector'
|
||||
import { workbenchOperation } from '@hcengineering/model-workbench'
|
||||
import { testManagementOperation } from '@hcengineering/model-test-management'
|
||||
|
||||
export const migrateOperations: [string, MigrateOperation][] = [
|
||||
['core', coreOperation],
|
||||
@ -92,5 +93,6 @@ export const migrateOperations: [string, MigrateOperation][] = [
|
||||
// We should call notification migration after activityServer and chunter
|
||||
['notification', notificationOperation],
|
||||
['analyticsCollector', analyticsCollectorOperation],
|
||||
['workbench', workbenchOperation]
|
||||
['workbench', workbenchOperation],
|
||||
['testManagement', testManagementOperation]
|
||||
]
|
||||
|
7
models/test-management/.eslintrc.js
Normal file
7
models/test-management/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
models/test-management/.npmignore
Normal file
4
models/test-management/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
5
models/test-management/config/rig.json
Normal file
5
models/test-management/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
59
models/test-management/package.json
Normal file
59
models/test-management/package.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@hcengineering/model-test-management",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"template": "@hcengineering/model-package",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/chunter": "^0.6.20",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/model": "^0.6.11",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-contact": "^0.6.1",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-print": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/model-workbench": "^0.6.1",
|
||||
"@hcengineering/model-activity": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.23",
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/setting": "^0.6.17",
|
||||
"@hcengineering/tags": "^0.6.16",
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/test-management": "^0.6.0",
|
||||
"@hcengineering/test-management-resources": "^0.6.0",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
"@hcengineering/model-preference": "^0.6.0"
|
||||
}
|
||||
}
|
51
models/test-management/src/defaultTypes.ts
Normal file
51
models/test-management/src/defaultTypes.ts
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
|
||||
import { TDefaultProjectTypeData } from './types'
|
||||
import testManagement from './plugin'
|
||||
|
||||
export function defineDefaultSpace (builder: Builder): void {
|
||||
defineDefaultProject(builder)
|
||||
}
|
||||
|
||||
function defineDefaultProject (builder: Builder): void {
|
||||
builder.createModel(TDefaultProjectTypeData)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.SpaceTypeDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
name: testManagement.string.TestProject,
|
||||
description: testManagement.string.FullDescription,
|
||||
icon: testManagement.icon.TestProject,
|
||||
baseClass: testManagement.class.TestProject,
|
||||
availablePermissions: [
|
||||
core.permission.UpdateSpace,
|
||||
core.permission.ArchiveSpace,
|
||||
core.permission.ForbidDeleteObject
|
||||
]
|
||||
},
|
||||
testManagement.descriptors.ProjectType
|
||||
)
|
||||
|
||||
builder.createDoc(core.class.SpaceType, core.space.Model, {
|
||||
name: 'Default project type',
|
||||
descriptor: testManagement.descriptors.ProjectType,
|
||||
roles: 0,
|
||||
targetClass: testManagement.mixin.DefaultProjectTypeData
|
||||
})
|
||||
}
|
422
models/test-management/src/index.ts
Normal file
422
models/test-management/src/index.ts
Normal file
@ -0,0 +1,422 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import core from '@hcengineering/model-core'
|
||||
import { SortingOrder } from '@hcengineering/core'
|
||||
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import print from '@hcengineering/model-print'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import { type ViewOptionsModel } from '@hcengineering/view'
|
||||
|
||||
import { testManagementId } from '@hcengineering/test-management'
|
||||
|
||||
import {
|
||||
DOMAIN_TEST_MANAGEMENT,
|
||||
TTypeTestCaseType,
|
||||
TTypeTestCasePriority,
|
||||
TTypeTestCaseStatus,
|
||||
TTestProject,
|
||||
TTestSuite,
|
||||
TTestCase,
|
||||
TDefaultProjectTypeData,
|
||||
TTestRun,
|
||||
TTypeTestRunResult,
|
||||
TTestRunItem
|
||||
} from './types'
|
||||
|
||||
import testManagement from './plugin'
|
||||
import { definePresenters } from './presenters'
|
||||
|
||||
export { testManagementId } from '@hcengineering/test-management/src/index'
|
||||
|
||||
function defineApplication (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: testManagement.string.TestManagementApplication,
|
||||
icon: testManagement.icon.TestManagementApplication,
|
||||
alias: testManagementId,
|
||||
hidden: false,
|
||||
locationResolver: testManagement.resolver.Location,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
id: 'projects',
|
||||
label: testManagement.string.Projects,
|
||||
spaceClass: testManagement.class.TestProject,
|
||||
addSpaceLabel: testManagement.string.CreateProject,
|
||||
createComponent: testManagement.component.CreateProject,
|
||||
icon: testManagement.icon.Home,
|
||||
specials: [
|
||||
{
|
||||
id: 'library',
|
||||
label: testManagement.string.TestLibrary,
|
||||
icon: testManagement.icon.TestLibrary,
|
||||
component: workbench.component.SpecialView,
|
||||
componentProps: {
|
||||
_class: testManagement.class.TestCase,
|
||||
icon: testManagement.icon.TestLibrary,
|
||||
label: testManagement.string.TestLibrary,
|
||||
createLabel: testManagement.string.CreateTestCase,
|
||||
createComponent: testManagement.component.CreateTestCase
|
||||
},
|
||||
navigationModel: {
|
||||
navigationComponent: view.component.FoldersBrowser,
|
||||
navigationComponentLabel: testManagement.string.TestSuites,
|
||||
navigationComponentIcon: testManagement.icon.TestSuites,
|
||||
mainComponentLabel: testManagement.string.TestCases,
|
||||
mainComponentIcon: testManagement.icon.TestCases,
|
||||
createComponent: testManagement.component.CreateTestSuite,
|
||||
navigationComponentProps: {
|
||||
_class: testManagement.class.TestSuite,
|
||||
icon: testManagement.icon.TestSuites,
|
||||
title: testManagement.string.TestSuites,
|
||||
createLabel: testManagement.string.CreateTestSuite,
|
||||
createComponent: testManagement.component.CreateTestSuite,
|
||||
titleKey: 'name',
|
||||
parentKey: 'parent',
|
||||
noParentId: testManagement.ids.NoParent,
|
||||
getFolderLink: testManagement.function.GetTestSuiteLink,
|
||||
allObjectsLabel: testManagement.string.AllTestCases,
|
||||
allObjectsIcon: testManagement.icon.TestSuites
|
||||
},
|
||||
syncWithLocationQuery: true
|
||||
}
|
||||
}
|
||||
/* TODO: UBERF-8584
|
||||
{
|
||||
id: opt.testRunsId,
|
||||
label: testManagement.string.TestRuns,
|
||||
icon: testManagement.icon.TestRuns,
|
||||
component: workbench.component.SpecialView,
|
||||
componentProps: {
|
||||
_class: testManagement.class.TestRun,
|
||||
icon: testManagement.icon.TestRuns,
|
||||
title: testManagement.string.TestRuns,
|
||||
createLabel: testManagement.string.NewTestRun,
|
||||
createComponent: testManagement.component.CreateTestRun
|
||||
}
|
||||
} */
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
navHeaderComponent: testManagement.component.NewTestCaseHeader
|
||||
},
|
||||
testManagement.app.TestManagement
|
||||
)
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TTypeTestCaseType,
|
||||
TTypeTestCasePriority,
|
||||
TTypeTestCaseStatus,
|
||||
TTestProject,
|
||||
TTestSuite,
|
||||
TTestCase,
|
||||
TDefaultProjectTypeData,
|
||||
TTestRun,
|
||||
TTestRunItem,
|
||||
TTypeTestRunResult
|
||||
)
|
||||
|
||||
builder.mixin(testManagement.class.TestProject, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestProject,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
defineTestSuite(builder)
|
||||
defineTestCase(builder)
|
||||
defineTestRun(builder)
|
||||
|
||||
definePresenters(builder)
|
||||
|
||||
defineApplication(builder)
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectIcon, {
|
||||
component: testManagement.component.TestCaseStatusPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_TEST_MANAGEMENT,
|
||||
disabled: [
|
||||
{ space: 1 },
|
||||
{ attachedToClass: 1 },
|
||||
{ status: 1 },
|
||||
{ project: 1 },
|
||||
{ priority: 1 },
|
||||
{ assignee: 1 },
|
||||
{ sprint: 1 },
|
||||
{ component: 1 },
|
||||
{ category: 1 },
|
||||
{ modifiedOn: 1 },
|
||||
{ modifiedBy: 1 },
|
||||
{ createdBy: 1 },
|
||||
{ relations: 1 },
|
||||
{ milestone: 1 },
|
||||
{ createdOn: -1 }
|
||||
]
|
||||
})
|
||||
|
||||
defineSpaceType(builder)
|
||||
}
|
||||
|
||||
function defineSpaceType (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.SpaceTypeDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
name: testManagement.string.TestProject,
|
||||
description: testManagement.string.FullDescription,
|
||||
icon: testManagement.icon.TestProject,
|
||||
baseClass: testManagement.class.TestProject,
|
||||
availablePermissions: [
|
||||
core.permission.UpdateSpace,
|
||||
core.permission.ArchiveSpace,
|
||||
core.permission.ForbidDeleteObject
|
||||
]
|
||||
},
|
||||
testManagement.descriptors.ProjectType
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.SpaceType,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Default project type',
|
||||
descriptor: testManagement.descriptors.ProjectType,
|
||||
roles: 0,
|
||||
targetClass: testManagement.mixin.DefaultProjectTypeData
|
||||
},
|
||||
testManagement.spaceType.DefaultProject
|
||||
)
|
||||
}
|
||||
|
||||
function defineTestSuite (builder: Builder): void {
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestSuite,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: testManagement.component.EditTestSuite
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: testManagement.component.EditTestSuite
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.TestSuitePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: testManagement.class.TestSuite,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', 'description'],
|
||||
configOptions: {
|
||||
strict: true
|
||||
}
|
||||
},
|
||||
testManagement.viewlet.TableTestSuites
|
||||
)
|
||||
|
||||
// Actions
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [
|
||||
view.action.Open,
|
||||
view.action.OpenInNewTab,
|
||||
print.action.Print,
|
||||
tracker.action.EditRelatedTargets,
|
||||
tracker.action.NewRelatedIssue
|
||||
]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: testManagement.actionImpl.CreateChildTestSuite,
|
||||
label: testManagement.string.CreateTestSuite,
|
||||
icon: testManagement.icon.TestSuite,
|
||||
category: testManagement.category.TestSuite,
|
||||
input: 'none',
|
||||
target: testManagement.class.TestSuite,
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
application: testManagement.app.TestManagement,
|
||||
group: 'create'
|
||||
}
|
||||
},
|
||||
testManagement.action.CreateChildTestSuite
|
||||
)
|
||||
}
|
||||
|
||||
function defineTestCase (builder: Builder): void {
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestCase,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: testManagement.component.EditTestCase
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: testManagement.component.EditTestCase
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.TestCasePresenter
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TypeTestCaseStatus, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: view.component.ValueFilter
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: testManagement.component.TestSuiteRefPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: testManagement.class.TestCase,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', { key: 'attachedTo', label: testManagement.string.TestSuite }, 'status', 'assignee'],
|
||||
configOptions: {
|
||||
strict: true
|
||||
}
|
||||
},
|
||||
testManagement.viewlet.TableTestCase
|
||||
)
|
||||
|
||||
const viewOptions: ViewOptionsModel = {
|
||||
groupBy: ['attachedTo'],
|
||||
orderBy: [
|
||||
['status', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['createdOn', SortingOrder.Descending]
|
||||
],
|
||||
other: [
|
||||
{
|
||||
key: 'shouldShowAll',
|
||||
type: 'toggle',
|
||||
defaultValue: false,
|
||||
actionTarget: 'category',
|
||||
action: view.function.ShowEmptyGroups,
|
||||
label: view.string.ShowEmptyGroups
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: testManagement.class.TestCase,
|
||||
descriptor: view.viewlet.List,
|
||||
configOptions: {
|
||||
strict: true,
|
||||
hiddenKeys: ['title']
|
||||
},
|
||||
config: [
|
||||
{ key: '', displayProps: { fixed: 'left', key: 'lead' } },
|
||||
{
|
||||
key: 'status',
|
||||
props: { kind: 'list', size: 'small', shouldShowName: false }
|
||||
},
|
||||
{ key: 'modifiedOn', displayProps: { key: 'modified', fixed: 'right', dividerBefore: true } },
|
||||
{
|
||||
key: 'assignee',
|
||||
props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' },
|
||||
displayProps: { key: 'assignee', fixed: 'right' }
|
||||
}
|
||||
],
|
||||
viewOptions
|
||||
},
|
||||
testManagement.viewlet.ListTestCase
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: testManagement.class.TestCase,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', 'assignee', 'modifiedOn'],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
},
|
||||
variant: 'short'
|
||||
},
|
||||
testManagement.viewlet.SuiteTestCases
|
||||
)
|
||||
}
|
||||
|
||||
function defineTestRun (builder: Builder): void {
|
||||
builder.mixin(testManagement.class.TestRun, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: testManagement.class.TestRun,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestRun, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: testManagement.component.EditTestRun
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestRun, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: testManagement.component.EditTestRun
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestRun, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.TestRunPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: testManagement.class.TestRun,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [''],
|
||||
configOptions: {
|
||||
strict: true
|
||||
}
|
||||
},
|
||||
testManagement.viewlet.TableTestRun
|
||||
)
|
||||
}
|
||||
|
||||
export { testManagementOperation } from './migration'
|
||||
export { default } from './plugin'
|
21
models/test-management/src/migration.ts
Normal file
21
models/test-management/src/migration.ts
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model'
|
||||
|
||||
export const testManagementOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (state: Map<string, Set<string>>, client: () => Promise<MigrationUpgradeClient>): Promise<void> {}
|
||||
}
|
49
models/test-management/src/plugin.ts
Normal file
49
models/test-management/src/plugin.ts
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { testManagementId } from '@hcengineering/test-management'
|
||||
import testManganement from '@hcengineering/test-management-resources/src/plugin'
|
||||
import type { Doc, Ref } from '@hcengineering/core'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { Action, ActionCategory, ViewAction } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(testManagementId, testManganement, {
|
||||
action: {
|
||||
DeleteTestCase: '' as Ref<Action<Doc, any>>,
|
||||
CreateChildTestSuite: '' as Ref<Action>,
|
||||
EditTestSuite: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
CreateChildTestSuite: '' as ViewAction,
|
||||
EditTestSuite: '' as ViewAction
|
||||
},
|
||||
category: {
|
||||
TestSuite: '' as Ref<ActionCategory>
|
||||
},
|
||||
component: {
|
||||
CreateTestCase: '' as AnyComponent,
|
||||
TestCasePresenter: '' as AnyComponent,
|
||||
ProjectPresenter: '' as AnyComponent,
|
||||
ProjectSpacePresenter: '' as AnyComponent,
|
||||
TestSuitePresenter: '' as AnyComponent,
|
||||
EditTestSuite: '' as AnyComponent,
|
||||
EditTestCase: '' as AnyComponent,
|
||||
CreateTestRun: '' as AnyComponent,
|
||||
TestRunPresenter: '' as AnyComponent,
|
||||
EditTestRun: '' as AnyComponent,
|
||||
TestSuiteRefPresenter: '' as AnyComponent
|
||||
}
|
||||
})
|
56
models/test-management/src/presenters.ts
Normal file
56
models/test-management/src/presenters.ts
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
import view from '@hcengineering/model-view'
|
||||
import testManagement from './plugin'
|
||||
|
||||
/**
|
||||
* Define presenters
|
||||
*/
|
||||
export function definePresenters (builder: Builder): void {
|
||||
//
|
||||
// Project
|
||||
//
|
||||
builder.mixin(testManagement.class.TestProject, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.ProjectPresenter
|
||||
})
|
||||
|
||||
builder.mixin(testManagement.class.TestProject, core.class.Class, view.mixin.SpacePresenter, {
|
||||
presenter: testManagement.component.ProjectSpacePresenter
|
||||
})
|
||||
|
||||
//
|
||||
// Test Suite
|
||||
//
|
||||
builder.mixin(testManagement.class.TestSuite, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.TestSuitePresenter
|
||||
})
|
||||
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: testManagement.component.TestCasePresenter
|
||||
})
|
||||
|
||||
//
|
||||
// Type Test Case Status
|
||||
//
|
||||
builder.mixin(testManagement.class.TypeTestCaseStatus, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: testManagement.component.TestCaseStatusPresenter
|
||||
})
|
||||
}
|
233
models/test-management/src/types.ts
Normal file
233
models/test-management/src/types.ts
Normal file
@ -0,0 +1,233 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Employee } from '@hcengineering/contact'
|
||||
import type {
|
||||
TestCase,
|
||||
TestSuite,
|
||||
TestCaseType,
|
||||
TestCasePriority,
|
||||
TestCaseStatus,
|
||||
TestProject,
|
||||
TestRun,
|
||||
TestRunResult,
|
||||
TestRunItem
|
||||
} from '@hcengineering/test-management'
|
||||
import { type Attachment } from '@hcengineering/attachment'
|
||||
import contact from '@hcengineering/contact'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import {
|
||||
Account,
|
||||
DateRangeMode,
|
||||
IndexKind,
|
||||
type RolesAssignment,
|
||||
type Role,
|
||||
Ref,
|
||||
type Domain,
|
||||
type Timestamp,
|
||||
type Type,
|
||||
type CollectionSize,
|
||||
type CollaborativeDoc,
|
||||
type Class
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
TypeRef,
|
||||
UX,
|
||||
TypeMarkup,
|
||||
Index,
|
||||
TypeCollaborativeDoc,
|
||||
TypeString,
|
||||
Collection,
|
||||
ReadOnly,
|
||||
TypeDate,
|
||||
Hidden
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import core, { TAttachedDoc, TDoc, TType, TTypedSpace } from '@hcengineering/model-core'
|
||||
|
||||
import testManagement from './plugin'
|
||||
|
||||
export { testManagementId } from '@hcengineering/test-management/src/index'
|
||||
|
||||
export const DOMAIN_TEST_MANAGEMENT = 'test-management' as Domain
|
||||
|
||||
/** @public */
|
||||
export function TypeTestCaseType (): Type<TestCaseType> {
|
||||
return { _class: testManagement.class.TypeTestCaseType, label: testManagement.string.TestCaseType }
|
||||
}
|
||||
|
||||
@Model(testManagement.class.TypeTestCaseType, core.class.Type, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestCaseType)
|
||||
export class TTypeTestCaseType extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeTestCasePriority (): Type<TestCasePriority> {
|
||||
return { _class: testManagement.class.TypeTestCasePriority, label: testManagement.string.TestCasePriority }
|
||||
}
|
||||
|
||||
@Model(testManagement.class.TypeTestCasePriority, core.class.Type, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestCasePriority)
|
||||
export class TTypeTestCasePriority extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeTestCaseStatus (): Type<TestCaseStatus> {
|
||||
return { _class: testManagement.class.TypeTestCaseStatus, label: testManagement.string.TestCaseStatus }
|
||||
}
|
||||
|
||||
@Model(testManagement.class.TypeTestCaseStatus, core.class.Type, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestCaseStatus)
|
||||
export class TTypeTestCaseStatus extends TType {}
|
||||
|
||||
@Model(testManagement.class.TestProject, core.class.TypedSpace)
|
||||
@UX(testManagement.string.TestProject)
|
||||
export class TTestProject extends TTypedSpace implements TestProject {
|
||||
@Prop(TypeMarkup(), testManagement.string.FullDescription)
|
||||
@Index(IndexKind.FullText)
|
||||
fullDescription?: string
|
||||
}
|
||||
|
||||
@Mixin(testManagement.mixin.DefaultProjectTypeData, testManagement.class.TestProject)
|
||||
@UX(getEmbeddedLabel('Default project'), testManagement.icon.TestProject)
|
||||
export class TDefaultProjectTypeData extends TTestProject implements RolesAssignment {
|
||||
[key: Ref<Role>]: Ref<Account>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@Model(testManagement.class.TestSuite, core.class.Doc, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestSuite, testManagement.icon.TestSuite, testManagement.string.TestSuite)
|
||||
export class TTestSuite extends TDoc implements TestSuite {
|
||||
@Prop(TypeString(), testManagement.string.SuiteName)
|
||||
@Index(IndexKind.FullText)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeMarkup(), testManagement.string.SuiteDescription)
|
||||
@Index(IndexKind.FullText)
|
||||
description?: string
|
||||
|
||||
@Prop(TypeRef(testManagement.class.TestSuite), testManagement.string.TestSuite)
|
||||
parent!: Ref<TestSuite>
|
||||
|
||||
@Prop(Collection(testManagement.class.TestCase), testManagement.string.TestCases, {
|
||||
shortLabel: testManagement.string.TestCase
|
||||
})
|
||||
testCases?: CollectionSize<TestCase>
|
||||
|
||||
declare space: Ref<TestProject>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@Model(testManagement.class.TestCase, core.class.AttachedDoc, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestCase, testManagement.icon.TestCase, testManagement.string.TestCase)
|
||||
export class TTestCase extends TAttachedDoc implements TestCase {
|
||||
@Prop(TypeRef(testManagement.class.TestProject), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<TestProject>
|
||||
|
||||
@Prop(TypeRef(testManagement.class.TestSuite), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
declare attachedTo: Ref<TestSuite>
|
||||
|
||||
@Prop(TypeRef(testManagement.class.TestSuite), core.string.AttachedToClass)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedToClass: Ref<Class<TestSuite>>
|
||||
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
override collection: 'testCases' = 'testCases'
|
||||
|
||||
@Prop(TypeString(), testManagement.string.TestName)
|
||||
@Index(IndexKind.FullText)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeCollaborativeDoc(), testManagement.string.FullDescription)
|
||||
@Index(IndexKind.FullText)
|
||||
description!: CollaborativeDoc
|
||||
|
||||
@Prop(TypeTestCaseType(), testManagement.string.TestType)
|
||||
@ReadOnly()
|
||||
type!: TestCaseType
|
||||
|
||||
@Prop(TypeTestCasePriority(), testManagement.string.TestPriority)
|
||||
@ReadOnly()
|
||||
priority!: TestCasePriority
|
||||
|
||||
@Prop(TypeTestCaseStatus(), testManagement.string.TestStatus)
|
||||
@ReadOnly()
|
||||
status!: TestCaseStatus
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), testManagement.string.TestAssignee)
|
||||
assignee!: Ref<Employee>
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: CollectionSize<Attachment>
|
||||
|
||||
@Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
comments?: number
|
||||
}
|
||||
|
||||
@Model(testManagement.class.TestRun, core.class.Doc, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestRun)
|
||||
export class TTestRun extends TDoc implements TestRun {
|
||||
@Prop(TypeString(), testManagement.string.TestRunName)
|
||||
@Index(IndexKind.FullText)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeCollaborativeDoc(), testManagement.string.FullDescription)
|
||||
@Index(IndexKind.FullText)
|
||||
description!: CollaborativeDoc
|
||||
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), testManagement.string.DueDate)
|
||||
dueDate?: Timestamp
|
||||
|
||||
@Prop(Collection(testManagement.class.TestRunItem), testManagement.string.TestRunItems, {
|
||||
shortLabel: testManagement.string.TestRunItem
|
||||
})
|
||||
items?: CollectionSize<TestRunItem>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function TypeTestRunResult (): Type<TestRunResult> {
|
||||
return { _class: testManagement.class.TypeTestRunResult, label: testManagement.string.TestRunResult }
|
||||
}
|
||||
|
||||
@Model(testManagement.class.TypeTestRunResult, core.class.Type, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestRunResult)
|
||||
export class TTypeTestRunResult extends TType {}
|
||||
|
||||
@Model(testManagement.class.TestRunItem, core.class.AttachedDoc, DOMAIN_TEST_MANAGEMENT)
|
||||
@UX(testManagement.string.TestRunItem)
|
||||
export class TTestRunItem extends TAttachedDoc implements TestRunItem {
|
||||
@Prop(TypeRef(testManagement.class.TestRun), testManagement.string.TestRun)
|
||||
testRun!: Ref<TestRun>
|
||||
|
||||
@Prop(TypeRef(testManagement.class.TestCase), testManagement.string.TestCase)
|
||||
testCase!: Ref<TestCase>
|
||||
|
||||
@Prop(TypeTestRunResult(), testManagement.string.TestRunResult)
|
||||
result?: TestRunResult
|
||||
|
||||
@Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments)
|
||||
comments?: number
|
||||
}
|
10
models/test-management/tsconfig.json
Normal file
10
models/test-management/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
@ -88,7 +88,8 @@ export default mergeIds(viewId, view, {
|
||||
ImageViewer: '' as AnyComponent,
|
||||
VideoViewer: '' as AnyComponent,
|
||||
PDFViewer: '' as AnyComponent,
|
||||
TextViewer: '' as AnyComponent
|
||||
TextViewer: '' as AnyComponent,
|
||||
FoldersBrowser: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Table: '' as IntlString,
|
||||
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "platform",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
38
packages/ui/src/components/SectionEmpty.svelte
Normal file
38
packages/ui/src/components/SectionEmpty.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { ComponentProps } from 'svelte'
|
||||
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let icon: ComponentProps<Icon>['icon']
|
||||
export let label: IntlString
|
||||
export let labelParams: Record<string, any> = {}
|
||||
</script>
|
||||
|
||||
<div class="antiSection-empty solid flex-col-center mt-3">
|
||||
<div class="flex-center caption-color">
|
||||
<Icon {icon} size="large" />
|
||||
</div>
|
||||
<span class="text-sm content-dark-color mt-2">
|
||||
<Label {label} params={labelParams} />
|
||||
</span>
|
||||
<slot />
|
||||
</div>
|
@ -272,6 +272,7 @@ export { default as TimeZonesPopup } from './components/TimeZonesPopup.svelte'
|
||||
export { default as CodeForm } from './components/CodeForm.svelte'
|
||||
export { default as CodeInput } from './components/CodeInput.svelte'
|
||||
export { default as TimeLeft } from './components/TimeLeft.svelte'
|
||||
export { default as SectionEmpty } from './components/SectionEmpty.svelte'
|
||||
|
||||
export { default as Dock } from './components/Dock.svelte'
|
||||
|
||||
|
7
plugins/test-management-assets/.eslintrc.js
Normal file
7
plugins/test-management-assets/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
52
plugins/test-management-assets/assets/icons.svg
Normal file
52
plugins/test-management-assets/assets/icons.svg
Normal file
@ -0,0 +1,52 @@
|
||||
<!-- ALL HASHTAGs -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="testManagementApplication" viewBox="0 0 24 24">
|
||||
<path d="M17.2,7.8h3.6c1.1,0,2-0.9,2-2V4.2c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0H9.8V4c0-1.5-1.2-2.8-2.8-2.8H4 C2.5,1.2,1.2,2.5,1.2,4v2c0,1.5,1.2,2.8,2.8,2.8h3c1.5,0,2.8-1.2,2.8-2.8V5.8h2V18c0,1.5,1.2,2.8,2.8,2.8h0.8v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-0.8c-0.7,0-1.2-0.6-1.2-1.2v-4.8h2v0c0,1.1,0.9,2,2,2h3.6 c1.1,0,2-0.9,2-2v-1.6c0-1.1-0.9-2-2-2h-3.6c-1.1,0-2,0.9-2,2v0h-2v-6h2v0C15.2,6.9,16.1,7.8,17.2,7.8z M8.2,6 c0,0.7-0.6,1.2-1.2,1.2H4C3.3,7.2,2.8,6.7,2.8,6V4c0-0.7,0.6-1.2,1.2-1.2h3c0.7,0,1.2,0.6,1.2,1.2V6z M16.8,19.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V19.2z M16.8,11.7 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V11.7z M16.8,4.2 c0-0.2,0.2-0.5,0.5-0.5h3.6c0.2,0,0.5,0.2,0.5,0.5v1.6c0,0.2-0.2,0.5-0.5,0.5h-3.6c-0.2,0-0.5-0.2-0.5-0.5V4.2z" />
|
||||
</symbol>
|
||||
<symbol id="testCase" viewBox="0 0 24 24">
|
||||
<path xmlns:default="http://www.w3.org/2000/svg" d="M3 9H1v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2H3V9zm15-4V3c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H5v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2V5h-5zm-6-2h4v2h-4V3zm0 12V8l5.5 3-5.5 4z" vector-effect="non-scaling-stroke"/>
|
||||
</symbol>
|
||||
<symbol id="testRun" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10 1.24988C8.26942 1.24988 6.57769 1.76306 5.13876 2.72452C3.69983 3.68598 2.57832 5.05254 1.91606 6.6514C1.25379 8.25025 1.08051 10.0096 1.41813 11.7069C1.75575 13.4043 2.58911 14.9634 3.81282 16.1871C5.03653 17.4108 6.59563 18.2441 8.29296 18.5817C9.9903 18.9194 11.7496 18.7461 13.3485 18.0838C14.9473 17.4216 16.3139 16.3 17.2754 14.8611C18.2368 13.4222 18.75 11.7305 18.75 9.99988C18.75 7.67923 17.8281 5.45364 16.1872 3.81269C14.5462 2.17175 12.3206 1.24988 10 1.24988ZM10 17.4999C8.51664 17.4999 7.0666 17.06 5.83323 16.2359C4.59986 15.4118 3.63856 14.2404 3.07091 12.87C2.50325 11.4996 2.35473 9.99156 2.64411 8.5367C2.9335 7.08184 3.64781 5.74547 4.6967 4.69658C5.7456 3.64768 7.08197 2.93338 8.53683 2.64399C9.99168 2.3546 11.4997 2.50312 12.8701 3.07078C14.2406 3.63844 15.4119 4.59973 16.236 5.8331C17.0601 7.06647 17.5 8.51652 17.5 9.99988C17.5 11.989 16.7098 13.8967 15.3033 15.3032C13.8968 16.7097 11.9891 17.4999 10 17.4999Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1919 7.05809C14.436 7.30217 14.436 7.6979 14.1919 7.94197L9.19194 12.942C8.94786 13.1861 8.55214 13.1861 8.30806 12.942L5.80806 10.442C5.56398 10.1979 5.56398 9.80217 5.80806 9.55809C6.05214 9.31401 6.44786 9.31401 6.69194 9.55809L8.75 11.6161L13.3081 7.05809C13.5521 6.81401 13.9479 6.81401 14.1919 7.05809Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="testSuite" viewBox="0 0 16 16">
|
||||
<path d="M14.5,6.7c0-2,0-3-0.7-3.8C13,2.2,12,2.2,10,2.2H6c-0.2,0-0.4,0-0.6,0l-0.1,0v0c-1.5,0-2.5,0.1-3.1,0.7 C1.5,3.6,1.5,4.7,1.5,6.7v2.7c0,2,0,3,0.7,3.8C3,13.8,4,13.8,6,13.8h4c2,0,3,0,3.8-0.7c0.5-0.5,0.7-1.3,0.7-2.4h0V10 c0-0.1,0-0.3,0-0.4c0-0.1,0-0.2,0-0.3V6.7z M13.1,3.6c0.4,0.4,0.4,1.3,0.4,3.1v0c-0.1-0.1-0.2-0.2-0.3-0.2c-0.6-0.3-1.2-0.3-2.5-0.3 c-0.7,0-1.1,0-1.3-0.1C9.2,6,9,5.9,8.9,5.8C8.7,5.6,8.5,5.3,8.2,4.6L7.6,3.5C7.6,3.3,7.5,3.3,7.4,3.2H10C11.7,3.2,12.6,3.2,13.1,3.6z M10,12.8H6c-1.7,0-2.6,0-3.1-0.4C2.5,12,2.5,11.1,2.5,9.3V6.7c0-1.7,0-2.6,0.4-3.1c0.4-0.4,1.1-0.4,2.5-0.4l0.1,0 c0.5,0,1,0.3,1.2,0.8l0.6,1.1c0.4,0.7,0.6,1.1,0.9,1.4C8.4,6.7,8.7,6.9,9,7c0.4,0.2,0.9,0.2,1.7,0.2c1.1,0,1.7,0,2,0.2 c0.2,0.1,0.4,0.3,0.6,0.6c0.2,0.3,0.2,0.9,0.2,1.9c0,1.4,0,2.2-0.4,2.6C12.6,12.8,11.7,12.8,10,12.8z"/>
|
||||
</symbol>
|
||||
<symbol id="home" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1L13.8838 5.29644C14.271 5.5788 14.5 6.0292 14.5 6.50845V12.5C14.5 13.3284 13.8284 14 13 14H3C2.17157 14 1.5 13.3284 1.5 12.5V6.50845C1.5 6.0292 1.729 5.5788 2.11624 5.29644L8 1Z"/>
|
||||
</symbol>
|
||||
<symbol id="red-circle" viewBox="0 0 14 14">
|
||||
<path d="M7,0C3.1,0,0,3.1,0,7c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C14,3.1,10.9,0,7,0z M7,12c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S9.8,12,7,12z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="status-draft" viewBox="1 1 14 14" fill="#D7D8DB">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 5.36133L8 2.73633L3.5 5.36133L3.5 10.6382L8 13.2632L12.5 10.6382L12.5 5.36133ZM8.75581 1.44066C8.28876 1.16822 7.71124 1.16822 7.24419 1.44066L2.74419 4.06566C2.28337 4.33448 2 4.82783 2 5.36133V10.6382C2 11.1717 2.28337 11.6651 2.74419 11.9339L7.24419 14.5589C7.71124 14.8313 8.28876 14.8313 8.75581 14.5589L13.2558 11.9339C13.7166 11.6651 14 11.1717 14 10.6382V5.36133C14 4.82783 13.7166 4.33448 13.2558 4.06566L8.75581 1.44066Z" />
|
||||
</symbol>
|
||||
<symbol id="status-review" viewBox="1 1 14 14" fill="#F2C94C">
|
||||
<path d="M8.3779 4.74233C8.14438 4.60607 7.85562 4.60607 7.6221 4.74233L5.37209 6.05513C5.14168 6.18957 5 6.4363 5 6.70311V9.34216C5 9.60897 5.14168 9.85573 5.37209 9.99016L7.6221 11.303C7.85562 11.4392 8.14438 11.4392 8.3779 11.303L10.6279 9.99016C10.8583 9.85573 11 9.60897 11 9.34216V6.70311C11 6.4363 10.8583 6.18957 10.6279 6.05513L8.3779 4.74233Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.24419 1.44066C7.71124 1.16822 8.28876 1.16822 8.75581 1.44066L13.2558 4.06566C13.7166 4.33448 14 4.82783 14 5.36133V10.6382C14 11.1717 13.7166 11.6651 13.2558 11.9339L8.75581 14.5589C8.28876 14.8313 7.71124 14.8313 7.24419 14.5589L2.74419 11.9339C2.28337 11.6651 2 11.1717 2 10.6382V5.36133C2 4.82783 2.28337 4.33448 2.74419 4.06566L7.24419 1.44066ZM8 2.73633L12.5 5.36133V10.6382L8 13.2632L3.5 10.6382V5.36133L8 2.73633Z" />
|
||||
</symbol>
|
||||
<symbol id="status-review-comments" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.75581 1.21148C8.28876 0.929507 7.71124 0.929507 7.24419 1.21148L2.74419 3.92829C2.28337 4.20651 2 4.71711 2 5.26927V10.7307C2 11.2829 2.28337 11.7935 2.74419 12.0717L7.24419 14.7885C7.71124 15.0705 8.28876 15.0705 8.75581 14.7885L13.2558 12.0717C13.7166 11.7935 14 11.2829 14 10.7307V5.26927C14 4.71711 13.7166 4.20651 13.2558 3.92829L8.75581 1.21148ZM12.5 5.26928L8 2.55246L3.5 5.26927L3.5 10.7307L8 13.4475L12.5 10.7307L12.5 5.26928Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5 5.75C6.91421 5.75 7.25 6.08579 7.25 6.5V9.5C7.25 9.91421 6.91421 10.25 6.5 10.25C6.08579 10.25 5.75 9.91421 5.75 9.5V6.5C5.75 6.08579 6.08579 5.75 6.5 5.75Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 5.75C9.91421 5.75 10.25 6.08579 10.25 6.5V9.5C10.25 9.91421 9.91421 10.25 9.5 10.25C9.08579 10.25 8.75 9.91421 8.75 9.5V6.5C8.75 6.08579 9.08579 5.75 9.5 5.75Z" />
|
||||
</symbol>
|
||||
<symbol id="status-approved" viewBox="1 1 14 14" fill="#5E6AD2">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 5.125L8 2.5L3.5 5.125L3.5 10.4019L8 13.0269L12.5 10.4019L12.5 5.125ZM8.75581 1.20433C8.28876 0.93189 7.71124 0.931889 7.24419 1.20433L2.74419 3.82933C2.28337 4.09815 2 4.5915 2 5.125V10.4019C2 10.9354 2.28337 11.4287 2.74419 11.6976L7.24419 14.3226C7.71124 14.595 8.28876 14.595 8.75581 14.3226L13.2558 11.6976C13.7166 11.4287 14 10.9354 14 10.4019V5.125C14 4.5915 13.7166 4.09815 13.2558 3.82933L8.75581 1.20433Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7381 5.69424C11.0526 5.96381 11.089 6.43728 10.8194 6.75178L7.81944 10.2518C7.68349 10.4104 7.48754 10.5051 7.27878 10.5131C7.07003 10.5212 6.86739 10.4417 6.71967 10.294L5.21967 8.79402C4.92678 8.50112 4.92678 8.02625 5.21967 7.73336C5.51256 7.44046 5.98744 7.44046 6.28033 7.73336L7.20764 8.66066L9.68056 5.77559C9.95012 5.4611 10.4236 5.42468 10.7381 5.69424Z" />
|
||||
</symbol>
|
||||
<symbol id="status-canceled" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<path d="M5.96967 5.96967C6.26256 5.67678 6.73744 5.67678 7.03033 5.96967L8 6.93934L8.96967 5.96967C9.26256 5.67678 9.73744 5.67678 10.0303 5.96967C10.3232 6.26256 10.3232 6.73744 10.0303 7.03033L9.06066 8L10.0303 8.96967C10.3232 9.26256 10.3232 9.73744 10.0303 10.0303C9.73744 10.3232 9.26256 10.3232 8.96967 10.0303L8 9.06066L7.03033 10.0303C6.73744 10.3232 6.26256 10.3232 5.96967 10.0303C5.67678 9.73744 5.67678 9.26256 5.96967 8.96967L6.93934 8L5.96967 7.03033C5.67678 6.73744 5.67678 6.26256 5.96967 5.96967Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.75581 1.21148C8.28876 0.929507 7.71124 0.929507 7.24419 1.21148L2.74419 3.92829C2.28337 4.20651 2 4.71711 2 5.26927V10.7307C2 11.2829 2.28337 11.7935 2.74419 12.0717L7.24419 14.7885C7.71124 15.0705 8.28876 15.0705 8.75581 14.7885L13.2558 12.0717C13.7166 11.7935 14 11.2829 14 10.7307V5.26927C14 4.71711 13.7166 4.20651 13.2558 3.92829L8.75581 1.21148ZM12.5 5.26928L8 2.55246L3.5 5.26927L3.5 10.7307L8 13.4475L12.5 10.7307L12.5 5.26928Z" />
|
||||
</symbol>
|
||||
<symbol id="document" viewBox="0 0 32 32">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6ZM10 17C10 16.4477 10.4477 16 11 16H21C21.5523 16 22 16.4477 22 17C22 17.5523 21.5523 18 21 18H11C10.4477 18 10 17.5523 10 17ZM10 23C10 22.4477 10.4477 22 11 22H21C21.5523 22 22 22.4477 22 23C22 23.5523 21.5523 24 21 24H11C10.4477 24 10 23.5523 10 23Z" />
|
||||
</symbol>
|
||||
<symbol id="test-library" viewBox="0 0 24 24">
|
||||
<path d="M21,2.2h-5h-5c-0.4,0-0.8,0.3-0.8,0.8v18c0,0.4,0.3,0.8,0.8,0.8h5h5c0.4,0,0.8-0.3,0.8-0.8V3C21.8,2.6,21.4,2.2,21,2.2z M11.8,3.8h3.5v16.5h-3.5V3.8z M20.2,20.2h-3.5V3.8h3.5V20.2z"/>
|
||||
<path d="M9.1,2.8l-4-0.5c-0.2,0-0.4,0-0.6,0.2S4.3,2.7,4.3,2.9l-2,17.5c0,0.4,0.2,0.8,0.7,0.8l4.2,0.5c0.2,0,0.4,0,0.6-0.2C7.9,21.5,8,21.3,8,21.1L9.7,3.6C9.8,3.2,9.5,2.8,9.1,2.8z M6.6,20.2l-2.7-0.3l1.8-16l2.5,0.3L6.6,20.2z"/>
|
||||
<path d="M18.5,9.8c0.4,0,0.8-0.3,0.8-0.8V7.5c0-0.4-0.3-0.8-0.8-0.8s-0.8,0.3-0.8,0.8V9C17.8,9.4,18.1,9.8,18.5,9.8z"/>
|
||||
<path d="M13.5,6.8c-0.4,0-0.8,0.3-0.8,0.8V9c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8V7.5C14.2,7.1,13.9,6.8,13.5,6.8z"/>
|
||||
</symbol>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
5
plugins/test-management-assets/config/rig.json
Normal file
5
plugins/test-management-assets/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "assets"
|
||||
}
|
7
plugins/test-management-assets/jest.config.js
Normal file
7
plugins/test-management-assets/jest.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
66
plugins/test-management-assets/lang/en.json
Normal file
66
plugins/test-management-assets/lang/en.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"string": {
|
||||
"ConfigLabel": "Test Management",
|
||||
"ConfigDescription": "Extension to manage test cases",
|
||||
"TestCaseType": "Type",
|
||||
"TestCasePriority": "Priority",
|
||||
"TestCaseStatus": "Status",
|
||||
"TestSuite": "Test Suite",
|
||||
"SuiteName": "Name",
|
||||
"SuiteDescription": "Description",
|
||||
"Suite": "Suite",
|
||||
"TestName": "Name",
|
||||
"TestDescription": "Description",
|
||||
"TestType": "Type",
|
||||
"TestPriority": "Priority",
|
||||
"TestStatus": "Status",
|
||||
"TestEstimatedTime": "Estimated time",
|
||||
"TestPreconditions": "Preconditions",
|
||||
"TestSteps": "Steps",
|
||||
"TestAssignee": "Assignee",
|
||||
"TestCase": "Test case",
|
||||
"TestProject": "Project",
|
||||
"TestManagementApplication": "Test Management",
|
||||
"AllTestCases": "All test cases",
|
||||
"AllProjects": "All projects",
|
||||
"Projects": "Projects",
|
||||
"CreateProject": "Create project",
|
||||
"EditProject": "Edit project",
|
||||
"TestCases": "Test cases",
|
||||
"TestManagementDescription": "Extension to manage test cases",
|
||||
"CreateTestCase": "New test case",
|
||||
"FullDescription": "Description",
|
||||
"ProjectName": "Name",
|
||||
"ProjectType": "Type",
|
||||
"Members": "Members",
|
||||
"RoleLabel": "Role",
|
||||
"ProjectMembers": "Project members",
|
||||
"TestSuites": "Test suites",
|
||||
"CreateTestSuite": "Create test suite",
|
||||
"NamePlaceholder": "Suite name",
|
||||
"DescriptionPlaceholder": "Description (optional)",
|
||||
"TestRuns": "Test runs",
|
||||
"NewTestRun": "New test run",
|
||||
"TestRun": "Test run",
|
||||
"TestNamePlaceholder": "Test case title",
|
||||
"ChooseIcon": "Choose icon",
|
||||
"NoTestSuite": "No test suite",
|
||||
"StatusDraft": "Draft",
|
||||
"StatusReview": "Ready for review",
|
||||
"StatusReviewComments": "Need to fix review comments",
|
||||
"StatusApproved": "Approved",
|
||||
"StatusRejected": "Rejected",
|
||||
"SetStatus": "Set status",
|
||||
"Assignee": "Assignee",
|
||||
"Unassigned": "Unassigned",
|
||||
"AssignTo": "Assign to",
|
||||
"AssignedTo": "Assigneed to",
|
||||
"PreviousAssigned": "Previously assigned",
|
||||
"NoTestCases": "There are no test cases in this test suite",
|
||||
"CreateTestRun": "Create test run",
|
||||
"TestRunNamePlaceholder": "Test run name",
|
||||
"SelectTestSuites": "Select test suites",
|
||||
"SelectTestCases": "Select test cases",
|
||||
"TestLibrary": "Test library"
|
||||
}
|
||||
}
|
66
plugins/test-management-assets/lang/fr.json
Normal file
66
plugins/test-management-assets/lang/fr.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"string": {
|
||||
"ConfigLabel": "Gestion des tests",
|
||||
"ConfigDescription": "Extension pour gérer les cas de tests",
|
||||
"TestCaseType": "Taper",
|
||||
"TestCasePriority": "Priorité",
|
||||
"TestCaseStatus": "Statut",
|
||||
"TestSuite": "Suite de tests",
|
||||
"SuiteName": "Nom",
|
||||
"SuiteDescription": "Description",
|
||||
"Suite": "Suite",
|
||||
"TestName": "Nom",
|
||||
"TestDescription": "Description",
|
||||
"TestType": "Taper",
|
||||
"TestPriority": "Priorité",
|
||||
"TestStatus": "Statut",
|
||||
"TestEstimatedTime": "Temps estimé",
|
||||
"TestPreconditions": "Conditions préalables",
|
||||
"TestSteps": "Mesures",
|
||||
"TestAssignee": "Cessionnaire",
|
||||
"TestCase": "Cas de test",
|
||||
"TestProject": "Projet",
|
||||
"TestManagementApplication": "Gestion des tests",
|
||||
"AllTestCases": "Tous les cas de tests",
|
||||
"AllProjects": "Tous les projets",
|
||||
"Projects": "Projets",
|
||||
"CreateProject": "Créer un projet",
|
||||
"EditProject": "Modifier le projet",
|
||||
"TestCases": "Cas de tests",
|
||||
"TestManagementDescription": "Extension pour gérer les cas de tests",
|
||||
"CreateTestCase": "Nouveau cas de test",
|
||||
"FullDescription": "Description",
|
||||
"ProjectName": "Nom",
|
||||
"ProjectType": "Taper",
|
||||
"Members": "Membres",
|
||||
"RoleLabel": "Rôle",
|
||||
"ProjectMembers": "Membres du projet",
|
||||
"TestSuites": "Suites de tests",
|
||||
"CreateTestSuite": "Créer une suite de tests",
|
||||
"NamePlaceholder": "Nom de la suite",
|
||||
"DescriptionPlaceholder": "Description (facultatif)",
|
||||
"TestRuns": "Exécutions de tests",
|
||||
"NewTestRun": "Nouveau test",
|
||||
"TestRun": "Exécution d'essai",
|
||||
"TestNamePlaceholder": "Titre du scénario de test",
|
||||
"ChooseIcon": "Choisir l'icône",
|
||||
"NoTestSuite": "Pas de suite de tests",
|
||||
"StatusDraft": "Brouillon",
|
||||
"StatusReview": "Prêt pour l'examen",
|
||||
"StatusReviewComments": "Besoin de corriger les commentaires d'évaluation",
|
||||
"StatusApproved": "Approuvé",
|
||||
"StatusRejected": "Rejeté",
|
||||
"SetStatus": "Définir le statut",
|
||||
"Assignee": "Attribué à",
|
||||
"Unassigned": "Non assigné",
|
||||
"AssignTo": "Assigner à...",
|
||||
"AssignedTo": "Assigné à {value}",
|
||||
"PreviousAssigned": "Assigné précédemment",
|
||||
"NoTestCases": "Il n'y a aucun cas de test dans cette suite de tests",
|
||||
"CreateTestRun": "Créer une série de tests",
|
||||
"TestRunNamePlaceholder": "Nom de l'exécution du test",
|
||||
"SelectTestSuites": "Sélectionnez les suites de tests",
|
||||
"SelectTestCases": "Sélectionnez des cas de test",
|
||||
"TestLibrary": "Bibliothèque de tests"
|
||||
}
|
||||
}
|
66
plugins/test-management-assets/lang/ru.json
Normal file
66
plugins/test-management-assets/lang/ru.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"string": {
|
||||
"ConfigLabel": "Управление тестированием",
|
||||
"ConfigDescription": "Расширение для управления тестирование",
|
||||
"TestCaseType": "Тип",
|
||||
"TestCasePriority": "Приоритет",
|
||||
"TestCaseStatus": "Статус",
|
||||
"TestSuite": "Тестовый набор",
|
||||
"SuiteName": "Имя",
|
||||
"SuiteDescription": "Описание",
|
||||
"Suite": "Тестовый набор",
|
||||
"TestName": "Имя",
|
||||
"TestDescription": "Описание",
|
||||
"TestType": "Тип",
|
||||
"TestPriority": "Приоритет",
|
||||
"TestStatus": "Статус",
|
||||
"TestEstimatedTime": "Расчетное время",
|
||||
"TestPreconditions": "Предварительные условия",
|
||||
"TestSteps": "Шаги",
|
||||
"TestAssignee": "Исполнитель",
|
||||
"TestCase": "Тест-кейс",
|
||||
"TestProject": "Проект",
|
||||
"TestManagementApplication": "Управление тестированием",
|
||||
"AllTestCases": "Все тест-кейсы",
|
||||
"AllProjects": "Все проекты",
|
||||
"Projects": "Проекты",
|
||||
"CreateProject": "Создать проект",
|
||||
"EditProject": "Отредактировать проект",
|
||||
"TestCases": "Тест-кейсы",
|
||||
"TestManagementDescription": "Расширение для управления тестирование",
|
||||
"CreateTestCase": "Новый тест-кейс",
|
||||
"FullDescription": "Описание",
|
||||
"ProjectName": "Имя",
|
||||
"ProjectType": "Тип",
|
||||
"Members": "Участники",
|
||||
"RoleLabel": "Роль",
|
||||
"ProjectMembers": "Участники проекта",
|
||||
"TestSuites": "Тестовые наборы",
|
||||
"CreateTestSuite": "Создать тестовый набор",
|
||||
"NamePlaceholder": "Имя тестового набора",
|
||||
"DescriptionPlaceholder": "Описание (опционально)",
|
||||
"TestRuns": "Выполнение тестов",
|
||||
"NewTestRun": "Новый тест план",
|
||||
"TestRun": "Тест план",
|
||||
"TestNamePlaceholder": "Имя тест кейса",
|
||||
"ChooseIcon": "Выберите иконку",
|
||||
"NoTestSuite": "Тестовый набор не задан",
|
||||
"StatusDraft": "В прогрессе",
|
||||
"StatusReview": "Готов для ревью",
|
||||
"StatusReviewComments": "Требует исправлений",
|
||||
"StatusApproved": "Согласован",
|
||||
"StatusRejected": "Отклонен",
|
||||
"SetStatus": "Выбрать статус",
|
||||
"Assignee": "Исполнитель",
|
||||
"Unassigned": "Не назначен",
|
||||
"AssignTo": "Назначить на",
|
||||
"AssignedTo": "Назначено на",
|
||||
"PreviousAssigned": "Ранее назначенные",
|
||||
"NoTestCases": "Тест-кейсы отсутствует",
|
||||
"CreateTestRun": "Создать тест план",
|
||||
"TestRunNamePlaceholder": "Название тест плана",
|
||||
"SelectTestSuites": "Выбрать наборы тестов",
|
||||
"SelectTestCases": "Выбрать тест-кейсы",
|
||||
"TestLibrary": "Библиотека тестов"
|
||||
}
|
||||
}
|
66
plugins/test-management-assets/lang/zh.json
Normal file
66
plugins/test-management-assets/lang/zh.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"string": {
|
||||
"ConfigLabel": "測試管理",
|
||||
"ConfigDescription": "管理測試用例的擴展",
|
||||
"TestCaseType": "類型",
|
||||
"TestCasePriority": "優先事項",
|
||||
"TestCaseStatus": "地位",
|
||||
"TestSuite": "測試套件",
|
||||
"SuiteName": "姓名",
|
||||
"SuiteDescription": "描述",
|
||||
"Suite": "Suite",
|
||||
"TestName": "姓名",
|
||||
"TestDescription": "描述",
|
||||
"TestType": "類型",
|
||||
"TestPriority": "優先事項",
|
||||
"TestStatus": "地位",
|
||||
"TestEstimatedTime": "預計時間",
|
||||
"TestPreconditions": "前提條件",
|
||||
"TestSteps": "步驟",
|
||||
"TestAssignee": "受讓人",
|
||||
"TestCase": "測試用例",
|
||||
"TestProject": "專案",
|
||||
"TestManagementApplication": "測試管理",
|
||||
"AllTestCases": "所有測試用例",
|
||||
"AllProjects": "所有項目",
|
||||
"Projects": "專案",
|
||||
"CreateProject": "創建專案",
|
||||
"EditProject": "編輯項目",
|
||||
"TestCases": "測試用例",
|
||||
"TestManagementDescription": "管理測試用例的擴展",
|
||||
"CreateTestCase": "新測試用例",
|
||||
"FullDescription": "描述",
|
||||
"ProjectName": "姓名",
|
||||
"ProjectType": "類型",
|
||||
"Members": "會員",
|
||||
"RoleLabel": "角色",
|
||||
"ProjectMembers": "專案成員",
|
||||
"TestSuites": "測試套件",
|
||||
"CreateTestSuite": "建立測試套件",
|
||||
"NamePlaceholder": "套房名稱",
|
||||
"DescriptionPlaceholder": "說明(可選)",
|
||||
"TestRuns": "試運行",
|
||||
"NewTestRun": "新試運行",
|
||||
"TestRun": "試運行",
|
||||
"TestNamePlaceholder": "測試用例標題",
|
||||
"ChooseIcon": "選擇圖示",
|
||||
"NoTestSuite": "沒有測試套件",
|
||||
"StatusDraft": "草稿",
|
||||
"StatusReview": "準備審查",
|
||||
"StatusReviewComments": "需要修復審核意見",
|
||||
"StatusApproved": "得到正式認可的",
|
||||
"StatusRejected": "被拒絕",
|
||||
"SetStatus": "設定狀態",
|
||||
"Assignee": "受讓人",
|
||||
"Unassigned": "未分配",
|
||||
"AssignTo": "分配給",
|
||||
"AssignedTo": "分配給",
|
||||
"PreviousAssigned": "先前分配的",
|
||||
"NoTestCases": "該測試套件中沒有測試案例",
|
||||
"CreateTestRun": "建立測試運行",
|
||||
"TestRunNamePlaceholder": "測試運行名稱",
|
||||
"SelectTestSuites": "選擇測試套件",
|
||||
"SelectTestCases": "選擇測試用例",
|
||||
"TestLibrary": "測試庫"
|
||||
}
|
||||
}
|
39
plugins/test-management-assets/package.json
Normal file
39
plugins/test-management-assets/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@hcengineering/test-management-assets",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"template": "@hcengineering/assets-package",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"build:docs": "",
|
||||
"format": "format src",
|
||||
"build:watch": "compile",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint": "^8.54.0",
|
||||
"prettier": "^3.1.0",
|
||||
"@types/node": "~20.11.16",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/test-management": "^0.6.0"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { makeLocalesTest } from '@hcengineering/platform'
|
||||
|
||||
it(
|
||||
'Locales are equale',
|
||||
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
|
||||
)
|
40
plugins/test-management-assets/src/index.ts
Normal file
40
plugins/test-management-assets/src/index.ts
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import testManagement from '@hcengineering/test-management'
|
||||
import { loadMetadata } from '@hcengineering/platform'
|
||||
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(testManagement.icon, {
|
||||
TestCase: `${icons}#testCase`,
|
||||
TestManagementApplication: `${icons}#testManagementApplication`,
|
||||
TestManagement: `${icons}#testCase`,
|
||||
TestManagementVersion: `${icons}#testCase`,
|
||||
TestCases: `${icons}#testCase`,
|
||||
Home: `${icons}#home`,
|
||||
Estimation: `${icons}#testCase`,
|
||||
TestSuite: `${icons}#testSuite`,
|
||||
TestProject: `${icons}#testCase`,
|
||||
TestSuites: `${icons}#testSuite`,
|
||||
TestRuns: `${icons}#testRun`,
|
||||
RedCircle: `${icons}#red-circle`,
|
||||
Document: `${icons}#document`,
|
||||
StatusDraft: `${icons}#status-draft`,
|
||||
StatusReview: `${icons}#status-review`,
|
||||
StatusReviewComments: `${icons}#status-review-comments`,
|
||||
StatusApproved: `${icons}#status-approved`,
|
||||
StatusRejected: `${icons}#status-canceled`,
|
||||
TestLibrary: `${icons}#test-library`
|
||||
})
|
11
plugins/test-management-assets/tsconfig.json
Normal file
11
plugins/test-management-assets/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"types": ["node", "jest"],
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
4
plugins/test-management-resources/.eslintrc.js
Normal file
4
plugins/test-management-resources/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname }
|
||||
}
|
22
plugins/test-management-resources/.prettierrc
Normal file
22
plugins/test-management-resources/.prettierrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"bracketSpacing": true,
|
||||
"proseWrap": "preserve",
|
||||
"plugins": [
|
||||
"prettier-plugin-svelte"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
5
plugins/test-management-resources/config/rig.json
Normal file
5
plugins/test-management-resources/config/rig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "ui"
|
||||
}
|
5
plugins/test-management-resources/jest.config.js
Normal file
5
plugins/test-management-resources/jest.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)']
|
||||
}
|
77
plugins/test-management-resources/package.json
Normal file
77
plugins/test-management-resources/package.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@hcengineering/test-management-resources",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile ui",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"svelte-check": "do-svelte-check",
|
||||
"_phase:svelte-check": "do-svelte-check",
|
||||
"format": "format src",
|
||||
"build:watch": "compile ui",
|
||||
"_phase:build": "compile ui",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svelte-loader": "^3.2.0",
|
||||
"sass": "^1.53.0",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"prettier-plugin-svelte": "^3.2.2",
|
||||
"eslint": "^8.54.0",
|
||||
"prettier": "^3.1.0",
|
||||
"svelte-check": "^3.6.9",
|
||||
"typescript": "^5.3.3",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"svelte-eslint-parser": "^0.33.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/activity-resources": "^0.6.1",
|
||||
"@hcengineering/analytics": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/attachment-resources": "^0.6.0",
|
||||
"@hcengineering/calendar": "^0.6.24",
|
||||
"@hcengineering/chunter": "^0.6.20",
|
||||
"@hcengineering/chunter-resources": "^0.6.0",
|
||||
"@hcengineering/client": "^0.6.18",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/contact-resources": "^0.6.0",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/kanban": "^0.6.0",
|
||||
"@hcengineering/login": "^0.6.12",
|
||||
"@hcengineering/notification": "^0.6.23",
|
||||
"@hcengineering/notification-resources": "^0.6.0",
|
||||
"@hcengineering/panel": "^0.6.23",
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/preference": "^0.6.13",
|
||||
"@hcengineering/presentation": "^0.6.3",
|
||||
"@hcengineering/query": "^0.6.12",
|
||||
"@hcengineering/setting": "^0.6.17",
|
||||
"@hcengineering/tags": "^0.6.16",
|
||||
"@hcengineering/task": "^0.6.20",
|
||||
"@hcengineering/task-resources": "^0.6.0",
|
||||
"@hcengineering/text-editor-resources": "^0.6.0",
|
||||
"@hcengineering/text": "^0.6.5",
|
||||
"@hcengineering/test-management": "^0.6.0",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/view-resources": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
"@hcengineering/workbench-resources": "^0.6.1",
|
||||
"fast-equals": "^5.0.1",
|
||||
"svelte": "^4.2.19"
|
||||
}
|
||||
}
|
5
plugins/test-management-resources/postcss.config.js
Normal file
5
plugins/test-management-resources/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="var(--duotone-color)"
|
||||
d="M13,7V3H9C7.1,3,6.2,3,5.6,3.6C5,4.2,5,5.1,5,7v10c0,1.9,0,2.8,0.6,3.4C6.2,21,7.1,21,9,21h6 c1.9,0,2.8,0,3.4-0.6S19,18.9,19,17V9h-4c-0.9,0-1.4,0-1.7-0.3C13,8.4,13,7.9,13,7z"
|
||||
/>
|
||||
<g {fill}>
|
||||
<path d="M9,12.4c-0.3,0-0.6,0.3-0.6,0.6s0.3,0.6,0.6,0.6h6c0.3,0,0.6-0.3,0.6-0.6s-0.3-0.6-0.6-0.6H9z" />
|
||||
<path d="M13,16.4H9c-0.3,0-0.6,0.3-0.6,0.6s0.3,0.6,0.6,0.6h4c0.3,0,0.6-0.3,0.6-0.6S13.3,16.4,13,16.4z" />
|
||||
<path
|
||||
d="M19.5,7.8c-0.1-0.3-0.3-0.5-0.6-0.8L15,3.2c-0.3-0.3-0.5-0.5-0.8-0.6s-0.6-0.1-1-0.1H9c-2,0-3.1,0-3.8,0.8 C4.4,3.9,4.4,5,4.4,7v10c0,2,0,3.1,0.8,3.8C5.9,21.6,7,21.6,9,21.6h6c2,0,3.1,0,3.8-0.8s0.8-1.9,0.8-3.8V8.8 C19.6,8.4,19.6,8.1,19.5,7.8z M13.6,3.6c0.1,0,0.1,0,0.1,0c0.1,0,0.2,0.2,0.4,0.4L18,7.8c0.2,0.2,0.3,0.3,0.4,0.4c0,0,0,0.1,0,0.1 H15c-0.7,0-1.2,0-1.3-0.1S13.6,7.7,13.6,7V3.6z M18.4,17c0,1.8,0,2.6-0.4,3c-0.4,0.4-1.2,0.4-3,0.4H9c-1.8,0-2.6,0-3-0.4 c-0.4-0.4-0.4-1.2-0.4-3V7c0-1.8,0-2.6,0.4-3c0.4-0.4,1.2-0.4,3-0.4h3.4V7c0,1,0,1.7,0.5,2.1C13.3,9.6,14,9.6,15,9.6h3.4V17z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
@ -0,0 +1,390 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { AccountArrayEditor } from '@hcengineering/contact-resources'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import core, {
|
||||
Account,
|
||||
Data,
|
||||
DocumentUpdate,
|
||||
RolesAssignment,
|
||||
Ref,
|
||||
Role,
|
||||
SpaceType,
|
||||
generateId,
|
||||
getCurrentAccount,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import view from '@hcengineering/view'
|
||||
import testManagement, { TestProject } from '@hcengineering/test-management'
|
||||
import presentation, { Card, getClient, reduceCalls } from '@hcengineering/presentation'
|
||||
import {
|
||||
Button,
|
||||
EditBox,
|
||||
IconWithEmoji,
|
||||
Label,
|
||||
Toggle,
|
||||
getColorNumberByText,
|
||||
showPopup,
|
||||
getPlatformColorDef,
|
||||
getPlatformColorForTextDef,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { IconPicker, SpaceTypeSelector } from '@hcengineering/view-resources'
|
||||
|
||||
import testManagementRes from '../../plugin'
|
||||
|
||||
export let project: TestProject | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let name: string = project?.name ?? ''
|
||||
let description: string = project?.description ?? ''
|
||||
let isPrivate: boolean = project?.private ?? false
|
||||
let icon: Asset | undefined = project?.icon ?? undefined
|
||||
let color = project?.color ?? getColorNumberByText(name)
|
||||
let isColorSelected = false
|
||||
|
||||
let members: Ref<Account>[] =
|
||||
project?.members !== undefined ? hierarchy.clone(project.members) : [getCurrentAccount()._id]
|
||||
let owners: Ref<Account>[] =
|
||||
project?.owners !== undefined ? hierarchy.clone(project.owners) : [getCurrentAccount()._id]
|
||||
let rolesAssignment: RolesAssignment = {}
|
||||
|
||||
let typeId: Ref<SpaceType> | undefined = project?.type ?? testManagementRes.spaceType.DefaultProject
|
||||
let spaceType: WithLookup<SpaceType> | undefined
|
||||
|
||||
$: void loadSpaceType(typeId)
|
||||
const loadSpaceType = reduceCalls(async (id: typeof typeId): Promise<void> => {
|
||||
spaceType =
|
||||
id !== undefined
|
||||
? await client
|
||||
.getModel()
|
||||
.findOne(core.class.SpaceType, { _id: id }, { lookup: { _id: { roles: core.class.Role } } })
|
||||
: undefined
|
||||
|
||||
if (project === undefined || spaceType?.targetClass === undefined || spaceType?.$lookup?.roles === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
rolesAssignment = getRolesAssignment()
|
||||
})
|
||||
|
||||
function getRolesAssignment (): RolesAssignment {
|
||||
if (project === undefined || spaceType?.targetClass === undefined || spaceType?.$lookup?.roles === undefined) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const asMixin = hierarchy.as(project, spaceType?.targetClass)
|
||||
|
||||
return spaceType.$lookup.roles.reduce<RolesAssignment>((prev, { _id }) => {
|
||||
prev[_id as Ref<Role>] = (asMixin as any)[_id] ?? []
|
||||
|
||||
return prev
|
||||
}, {})
|
||||
}
|
||||
|
||||
async function handleSave (): Promise<void> {
|
||||
if (project === undefined) {
|
||||
await createTestProject()
|
||||
} else {
|
||||
await updateTestProject()
|
||||
}
|
||||
}
|
||||
|
||||
function getTestProjectData (): Omit<Data<TestProject>, 'type'> {
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
private: isPrivate,
|
||||
icon,
|
||||
members,
|
||||
owners,
|
||||
archived: false
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTestProject (): Promise<void> {
|
||||
if (project === undefined || spaceType?.targetClass === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const data = getTestProjectData()
|
||||
const update: DocumentUpdate<TestProject> = {}
|
||||
if (data.name !== project?.name) {
|
||||
update.name = data.name
|
||||
}
|
||||
if (data.description !== project?.description) {
|
||||
update.description = data.description
|
||||
}
|
||||
if (data.private !== project?.private) {
|
||||
update.private = data.private
|
||||
}
|
||||
if (data.icon !== project?.icon) {
|
||||
update.icon = data.icon
|
||||
}
|
||||
if (data.members.length !== project?.members.length) {
|
||||
update.members = data.members
|
||||
} else {
|
||||
for (const member of data.members) {
|
||||
if (project.members.findIndex((p) => p === member) === -1) {
|
||||
update.members = data.members
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.owners?.length !== project?.owners?.length) {
|
||||
update.owners = data.owners
|
||||
} else {
|
||||
for (const owner of data.owners ?? []) {
|
||||
if (project.owners?.findIndex((p) => p === owner) === -1) {
|
||||
update.owners = data.owners
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(update).length > 0) {
|
||||
await client.update(project, update)
|
||||
}
|
||||
|
||||
if (!deepEqual(rolesAssignment, getRolesAssignment())) {
|
||||
await client.updateMixin(
|
||||
project._id,
|
||||
testManagementRes.class.TestProject,
|
||||
core.space.Space,
|
||||
spaceType.targetClass,
|
||||
rolesAssignment
|
||||
)
|
||||
}
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
async function createTestProject (): Promise<void> {
|
||||
if (typeId === undefined || spaceType?.targetClass === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const projectId = generateId<TestProject>()
|
||||
const projectData = getTestProjectData()
|
||||
|
||||
await client.createDoc(
|
||||
testManagementRes.class.TestProject,
|
||||
core.space.Space,
|
||||
{ ...projectData, type: typeId },
|
||||
projectId
|
||||
)
|
||||
|
||||
// Create space type's mixin with roles assignments
|
||||
await client.createMixin(
|
||||
projectId,
|
||||
testManagementRes.class.TestProject,
|
||||
core.space.Space,
|
||||
spaceType.targetClass,
|
||||
rolesAssignment
|
||||
)
|
||||
// TODO: Analytics.handleEvent(TestProjectEvents.TestProjectCreated, { id: projectId })
|
||||
close(projectId)
|
||||
}
|
||||
|
||||
function close (id?: Ref<TestProject>): void {
|
||||
dispatch('close', id)
|
||||
}
|
||||
|
||||
function handleTypeChange (evt: CustomEvent<Ref<SpaceType>>): void {
|
||||
typeId = evt.detail
|
||||
}
|
||||
|
||||
$: roles = (spaceType?.$lookup?.roles ?? []) as Role[]
|
||||
|
||||
function handleOwnersChanged (newOwners: Ref<Account>[]): void {
|
||||
owners = newOwners
|
||||
|
||||
const newMembersSet = new Set([...members, ...newOwners])
|
||||
members = Array.from(newMembersSet)
|
||||
}
|
||||
|
||||
function handleMembersChanged (newMembers: Ref<Account>[]): void {
|
||||
// If a member was removed we need to remove it from any roles assignments as well
|
||||
const newMembersSet = new Set(newMembers)
|
||||
const removedMembersSet = new Set(members.filter((m) => !newMembersSet.has(m)))
|
||||
|
||||
if (removedMembersSet.size > 0 && rolesAssignment !== undefined) {
|
||||
for (const [key, value] of Object.entries(rolesAssignment)) {
|
||||
rolesAssignment[key as Ref<Role>] = value != null ? value.filter((m) => !removedMembersSet.has(m)) : undefined
|
||||
}
|
||||
}
|
||||
|
||||
members = newMembers
|
||||
}
|
||||
|
||||
function chooseIcon (ev: MouseEvent): void {
|
||||
const icons = [testManagement.icon.Home, testManagement.icon.RedCircle]
|
||||
const update = (result: any): void => {
|
||||
if (result !== undefined && result !== null) {
|
||||
icon = result.icon
|
||||
color = result.color
|
||||
isColorSelected = true
|
||||
}
|
||||
}
|
||||
showPopup(IconPicker, { icon, color, icons }, 'top', update, update)
|
||||
}
|
||||
|
||||
function handleRoleAssignmentChanged (roleId: Ref<Role>, newMembers: Ref<Account>[]): void {
|
||||
if (rolesAssignment === undefined) {
|
||||
rolesAssignment = {}
|
||||
}
|
||||
|
||||
rolesAssignment[roleId] = newMembers
|
||||
}
|
||||
|
||||
$: canSave =
|
||||
name.trim().length > 0 &&
|
||||
!(members.length === 0 && isPrivate) &&
|
||||
typeId !== undefined &&
|
||||
spaceType?.targetClass !== undefined &&
|
||||
owners.length > 0 &&
|
||||
(!isPrivate || owners.some((o) => members.includes(o)))
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={project ? testManagementRes.string.EditProject : testManagementRes.string.CreateProject}
|
||||
okLabel={project ? presentation.string.Save : presentation.string.Create}
|
||||
okAction={handleSave}
|
||||
{canSave}
|
||||
accentHeader
|
||||
width={'medium'}
|
||||
gap={'gapV-6'}
|
||||
onCancel={close}
|
||||
on:changeContent
|
||||
>
|
||||
<div class="antiGrid">
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={core.string.SpaceType} />
|
||||
</div>
|
||||
|
||||
<SpaceTypeSelector
|
||||
disabled={project !== undefined}
|
||||
descriptors={[testManagementRes.descriptors.ProjectType]}
|
||||
type={typeId}
|
||||
focusIndex={4}
|
||||
kind="regular"
|
||||
size="large"
|
||||
on:change={handleTypeChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={core.string.Name} />
|
||||
</div>
|
||||
<div class="padding">
|
||||
<EditBox id="teamspace-title" bind:value={name} placeholder={core.string.Name} kind={'large-style'} autoFocus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header topAlign">
|
||||
<Label label={core.string.Description} />
|
||||
</div>
|
||||
<div class="padding">
|
||||
<EditBox id="teamspace-description" bind:value={description} placeholder={core.string.Description} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="antiGrid">
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={testManagementRes.string.ChooseIcon} />
|
||||
</div>
|
||||
<Button
|
||||
icon={icon === view.ids.IconWithEmoji ? IconWithEmoji : icon ?? testManagement.icon.Home}
|
||||
iconProps={icon === view.ids.IconWithEmoji
|
||||
? { icon: color }
|
||||
: {
|
||||
fill:
|
||||
color !== undefined
|
||||
? getPlatformColorDef(color, $themeStore.dark).icon
|
||||
: getPlatformColorForTextDef(name, $themeStore.dark).icon
|
||||
}}
|
||||
size={'large'}
|
||||
on:click={chooseIcon}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="antiGrid">
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={core.string.Owners} />
|
||||
</div>
|
||||
<AccountArrayEditor
|
||||
value={owners}
|
||||
label={core.string.Owners}
|
||||
onChange={handleOwnersChanged}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header withDesciption">
|
||||
<Label label={presentation.string.MakePrivate} />
|
||||
<span><Label label={presentation.string.MakePrivateDescription} /></span>
|
||||
</div>
|
||||
<Toggle bind:on={isPrivate} disabled={!isPrivate && members.length === 0} />
|
||||
</div>
|
||||
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={core.string.Members} />
|
||||
</div>
|
||||
<AccountArrayEditor
|
||||
value={members}
|
||||
label={core.string.Members}
|
||||
onChange={handleMembersChanged}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
allowGuests
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#each roles as role}
|
||||
<div class="antiGrid-row">
|
||||
<div class="antiGrid-row__header">
|
||||
<Label label={testManagementRes.string.RoleLabel} params={{ role: role.name }} />
|
||||
</div>
|
||||
<AccountArrayEditor
|
||||
value={rolesAssignment?.[role._id] ?? []}
|
||||
label={core.string.Members}
|
||||
includeItems={members}
|
||||
readonly={members.length === 0}
|
||||
onChange={(refs) => {
|
||||
handleRoleAssignmentChanged(role._id, refs)
|
||||
}}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div></Card
|
||||
>
|
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TestProject } from '@hcengineering/test-management'
|
||||
|
||||
export let value: TestProject | undefined
|
||||
export let inline: boolean = false
|
||||
export let accent: boolean = false
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="flex-presenter cursor-default" class:inline-presenter={inline}>
|
||||
<span class="label no-underline nowrap" class:fs-bold={accent}>
|
||||
{value.name}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,105 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { TestProject } from '@hcengineering/test-management'
|
||||
import {
|
||||
IconWithEmoji,
|
||||
getPlatformColorDef,
|
||||
getPlatformColorForTextDef,
|
||||
themeStore,
|
||||
type Action
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { NavLink, TreeNode } from '@hcengineering/view-resources'
|
||||
import { SpacesNavModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||
import { SpecialElement } from '@hcengineering/workbench-resources'
|
||||
|
||||
export let space: TestProject
|
||||
export let model: SpacesNavModel
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentSpecial: string | undefined
|
||||
export let getActions: (space: TestProject) => Promise<Action[]> = async () => []
|
||||
export let deselect: boolean = false
|
||||
export let forciblyСollapsed: boolean = false
|
||||
|
||||
let specials: SpecialNavModel[] = []
|
||||
|
||||
async function updateSpecials (model: SpacesNavModel, space: TestProject): Promise<void> {
|
||||
const newSpecials: SpecialNavModel[] = []
|
||||
for (const sp of model.specials ?? []) {
|
||||
let shouldAdd = true
|
||||
if (sp.visibleIf !== undefined) {
|
||||
const visibleIf = await getResource(sp.visibleIf)
|
||||
if (visibleIf !== undefined) {
|
||||
shouldAdd = await visibleIf([space])
|
||||
}
|
||||
}
|
||||
if (shouldAdd) {
|
||||
newSpecials.push(sp)
|
||||
}
|
||||
}
|
||||
specials = newSpecials
|
||||
}
|
||||
|
||||
$: updateSpecials(model, space)
|
||||
$: visible =
|
||||
(!deselect && currentSpace !== undefined && currentSpecial !== undefined && space._id === currentSpace) ||
|
||||
forciblyСollapsed
|
||||
</script>
|
||||
|
||||
{#if specials}
|
||||
<TreeNode
|
||||
_id={space?._id}
|
||||
icon={space?.icon === view.ids.IconWithEmoji ? IconWithEmoji : space?.icon ?? model.icon}
|
||||
iconProps={space?.icon === view.ids.IconWithEmoji
|
||||
? { icon: space.color }
|
||||
: {
|
||||
fill:
|
||||
space.color !== undefined
|
||||
? getPlatformColorDef(space.color, $themeStore.dark).icon
|
||||
: getPlatformColorForTextDef(space.name, $themeStore.dark).icon
|
||||
}}
|
||||
title={space.name}
|
||||
type={'nested'}
|
||||
highlighted={space._id === currentSpace}
|
||||
{visible}
|
||||
actions={() => getActions(space)}
|
||||
{forciblyСollapsed}
|
||||
>
|
||||
{#each specials as special}
|
||||
<NavLink space={space._id} special={special.id}>
|
||||
<SpecialElement
|
||||
indent
|
||||
label={special.label}
|
||||
icon={special.icon}
|
||||
selected={deselect ? false : currentSpace === space._id && special.id === currentSpecial}
|
||||
/>
|
||||
</NavLink>
|
||||
{/each}
|
||||
|
||||
<svelte:fragment slot="visible">
|
||||
{#if visible}
|
||||
{@const item = specials.find((sp) => sp.id === currentSpecial && currentSpace === space._id)}
|
||||
{#if item}
|
||||
<NavLink space={space._id} special={item.id}>
|
||||
<SpecialElement indent label={item.label} icon={item.icon} selected forciblyСollapsed />
|
||||
</NavLink>
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</TreeNode>
|
||||
{/if}
|
@ -0,0 +1,198 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact'
|
||||
import { AssigneeBox, AssigneePopup, personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import { AssigneeCategory } from '@hcengineering/contact-resources/src/assignee'
|
||||
import { Account, Doc, DocumentQuery, Ref, Space, generateId } from '@hcengineering/core'
|
||||
import { RuleApplyResult, getClient, getDocRules } from '@hcengineering/presentation'
|
||||
import { TestCase } from '@hcengineering/test-management'
|
||||
import { ButtonKind, ButtonSize, IconSize, TooltipAlignment } from '@hcengineering/ui'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
import testManagement from '../../plugin'
|
||||
import { getPreviousAssignees } from '../../utils'
|
||||
|
||||
type AssigneeObject = (Doc | any) & Pick<TestCase, 'assignee'>
|
||||
|
||||
export let object: AssigneeObject | AssigneeObject[] | undefined = undefined
|
||||
export let value: AssigneeObject | AssigneeObject[] | undefined = undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
export let avatarSize: IconSize = 'card'
|
||||
export let tooltipAlignment: TooltipAlignment | undefined = undefined
|
||||
export let width: string = 'min-content'
|
||||
export let focusIndex: number | undefined = undefined
|
||||
export let short: boolean = false
|
||||
export let shouldShowName = true
|
||||
export let shrink: number = 0
|
||||
export let isAction: boolean = false
|
||||
export let readonly: boolean = false
|
||||
export let showStatus = true
|
||||
|
||||
$: _object =
|
||||
(typeof object !== 'string' ? object : undefined) ?? (typeof value !== 'string' ? value : undefined) ?? []
|
||||
|
||||
$: docs = Array.isArray(_object) ? _object : [_object]
|
||||
$: cdocs = docs.filter((d) => '_class' in d) as Doc[]
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
let progress = false
|
||||
|
||||
const handleAssigneeChanged = async (newAssignee: Ref<Person> | undefined | null) => {
|
||||
if (newAssignee === undefined || (!Array.isArray(_object) && _object?.assignee === newAssignee)) {
|
||||
return
|
||||
}
|
||||
progress = true
|
||||
const ops = client.apply()
|
||||
if (Array.isArray(_object)) {
|
||||
for (const p of _object) {
|
||||
if ('_class' in p) {
|
||||
// Analytics.handleEvent(TrackerEvents.IssueSetAssignee, { issue: p.identifier ?? p._id })
|
||||
await ops.update(p, { assignee: newAssignee })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ('_class' in _object) {
|
||||
// Analytics.handleEvent(TrackerEvents.IssueSetAssignee, { issue: _object.identifier ?? _object._id })
|
||||
await ops.update(_object, { assignee: newAssignee })
|
||||
}
|
||||
}
|
||||
|
||||
await ops.commit()
|
||||
|
||||
progress = false
|
||||
|
||||
dispatch('change', newAssignee)
|
||||
if (isAction) dispatch('close')
|
||||
}
|
||||
|
||||
let categories: AssigneeCategory[] = []
|
||||
|
||||
function getCategories (object: AssigneeObject | AssigneeObject[]): void {
|
||||
categories = []
|
||||
if (cdocs.length > 0) {
|
||||
categories.push({
|
||||
label: testManagement.string.PreviousAssigned,
|
||||
func: async () => {
|
||||
const r: Ref<Person>[] = []
|
||||
for (const d of cdocs) {
|
||||
r.push(...(await getPreviousAssignees(d._id)))
|
||||
}
|
||||
return r
|
||||
}
|
||||
})
|
||||
}
|
||||
categories.push({
|
||||
label: testManagement.string.Members,
|
||||
func: async () => {
|
||||
const spaces = Array.from(docs.map((it) => it.space).filter((it) => it)) as Ref<Space>[]
|
||||
if (spaces.length === 0) {
|
||||
return []
|
||||
}
|
||||
const projects = await client.findAll(testManagement.class.TestProject, {
|
||||
_id: !Array.isArray(object) ? object.space : { $in: Array.from(object.map((it) => it.space)) }
|
||||
})
|
||||
if (projects === undefined) {
|
||||
return []
|
||||
}
|
||||
const store = get(personAccountByIdStore)
|
||||
const allMembers = projects.reduce((arr, p) => arr.concat(p.members), [] as Ref<Account>[])
|
||||
const accounts = allMembers
|
||||
.map((p) => store.get(p as Ref<PersonAccount>))
|
||||
.filter((p) => p !== undefined) as PersonAccount[]
|
||||
return accounts.map((p) => p.person as Ref<Employee>)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$: getCategories(_object)
|
||||
|
||||
$: sel =
|
||||
(!Array.isArray(_object)
|
||||
? _object.assignee
|
||||
: _object.reduce((v, it) => (v != null && v === it.assignee ? it.assignee : null), _object[0]?.assignee) ??
|
||||
undefined) ?? undefined
|
||||
|
||||
let rulesQuery: RuleApplyResult<Employee> | undefined
|
||||
let query: DocumentQuery<Employee>
|
||||
$: if (cdocs.length > 0) {
|
||||
rulesQuery = getDocRules<Employee>(cdocs, 'assignee')
|
||||
if (rulesQuery !== undefined) {
|
||||
query = { ...(rulesQuery?.fieldQuery ?? {}), active: true }
|
||||
} else {
|
||||
query = { _id: 'none' as Ref<Employee>, active: true }
|
||||
rulesQuery = {
|
||||
disableEdit: true,
|
||||
disableUnset: true,
|
||||
fieldQuery: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if _object}
|
||||
{#if isAction}
|
||||
<AssigneePopup
|
||||
docQuery={query}
|
||||
{categories}
|
||||
icon={contact.icon.Person}
|
||||
selected={sel}
|
||||
allowDeselect={true}
|
||||
titleDeselect={undefined}
|
||||
loading={progress}
|
||||
on:close={(evt) => {
|
||||
const result = evt.detail
|
||||
if (result === null) {
|
||||
handleAssigneeChanged(null)
|
||||
} else if (result !== undefined && result._id !== value) {
|
||||
value = result._id
|
||||
handleAssigneeChanged(result._id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<AssigneeBox
|
||||
docQuery={query}
|
||||
{focusIndex}
|
||||
label={testManagement.string.Assignee}
|
||||
placeholder={testManagement.string.Assignee}
|
||||
value={sel}
|
||||
{categories}
|
||||
titleDeselect={testManagement.string.Unassigned}
|
||||
{size}
|
||||
{kind}
|
||||
{avatarSize}
|
||||
{width}
|
||||
{short}
|
||||
{shrink}
|
||||
{readonly}
|
||||
{shouldShowName}
|
||||
{showStatus}
|
||||
showNavigate={false}
|
||||
justify={'left'}
|
||||
showTooltip={{
|
||||
label: testManagement.string.AssignTo,
|
||||
personLabel: testManagement.string.AssignedTo,
|
||||
placeholderLabel: testManagement.string.Unassigned,
|
||||
direction: tooltipAlignment
|
||||
}}
|
||||
on:change={({ detail }) => handleAssigneeChanged(detail)}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
@ -0,0 +1,200 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentPresenter, AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { TestCase, TestProject, TestSuite, TestCaseStatus } from '@hcengineering/test-management'
|
||||
import core, { fillDefaults, generateId, makeCollaborativeDoc, Ref, TxOperations, Data } from '@hcengineering/core'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { Card, SpaceSelector, getClient, updateMarkup } from '@hcengineering/presentation'
|
||||
import { EmptyMarkup } from '@hcengineering/text'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, IconAttachment, getLocation } from '@hcengineering/ui'
|
||||
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import AssigneeEditor from './AssigneeEditor.svelte'
|
||||
import ProjectPresenter from '../project/ProjectPresenter.svelte'
|
||||
import testManagement from '../../plugin'
|
||||
|
||||
export let onCreate: ((orgId: Ref<TestCase>, client: TxOperations) => Promise<void>) | undefined = undefined
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.name === ''
|
||||
}
|
||||
|
||||
export let space: Ref<TestProject>
|
||||
|
||||
export let testSuiteId: Ref<TestSuite> | undefined
|
||||
|
||||
testSuiteId = testSuiteId ?? (getLocation()?.query?.attachedTo as Ref<TestSuite>)
|
||||
const id: Ref<TestCase> = generateId()
|
||||
|
||||
const object: Data<TestCase> = {
|
||||
name: '',
|
||||
description: makeCollaborativeDoc(id, 'description'),
|
||||
status: TestCaseStatus.Draft,
|
||||
assignee: null,
|
||||
attachments: 0,
|
||||
attachedTo: testSuiteId
|
||||
} as unknown as TestCase
|
||||
|
||||
let _space = space
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let description = EmptyMarkup
|
||||
|
||||
fillDefaults(hierarchy, object, testManagement.class.TestCase)
|
||||
|
||||
async function createTestCase (): Promise<void> {
|
||||
const op = client.apply()
|
||||
await updateMarkup(object.description, { description })
|
||||
await op.addCollection(
|
||||
testManagement.class.TestCase,
|
||||
_space,
|
||||
testSuiteId ?? testManagement.ids.NoParent,
|
||||
testManagement.class.TestSuite,
|
||||
'testCases',
|
||||
object,
|
||||
id
|
||||
)
|
||||
await descriptionBox.createAttachments(id, op)
|
||||
|
||||
if (onCreate !== undefined) {
|
||||
await onCreate?.(id, op)
|
||||
}
|
||||
await op.commit()
|
||||
dispatch('close', id)
|
||||
}
|
||||
|
||||
function handleTestSuiteChange (evt: CustomEvent<Ref<TestSuite>>): void {
|
||||
object.attachedTo = evt.detail
|
||||
}
|
||||
|
||||
const manager = createFocusManager()
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
<Card
|
||||
label={testManagement.string.CreateTestCase}
|
||||
okAction={createTestCase}
|
||||
hideAttachments={attachments.size === 0}
|
||||
canSave={object.name.length > 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
on:changeContent
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector
|
||||
_class={testManagement.class.TestProject}
|
||||
label={testManagement.string.TestProject}
|
||||
bind:space={_space}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
component={ProjectPresenter}
|
||||
defaultIcon={testManagement.icon.Home}
|
||||
/>
|
||||
<ObjectBox
|
||||
_class={testManagement.class.TestSuite}
|
||||
value={testSuiteId}
|
||||
docQuery={{
|
||||
space: _space
|
||||
}}
|
||||
on:change={handleTestSuiteChange}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
label={testManagement.string.NoTestSuite}
|
||||
icon={testManagement.icon.TestSuite}
|
||||
searchField={'title'}
|
||||
allowDeselect={true}
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
focusIndex={20000}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex-row-center clear-mins mb-3">
|
||||
<EditBox
|
||||
bind:value={object.name}
|
||||
placeholder={testManagement.string.TestNamePlaceholder}
|
||||
kind={'large-style'}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
objectId={id}
|
||||
_class={testManagement.class.TestCase}
|
||||
space={_space}
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
bind:content={description}
|
||||
placeholder={core.string.Description}
|
||||
kind="indented"
|
||||
isScrollable={false}
|
||||
enableBackReferences={true}
|
||||
enableAttachments={false}
|
||||
on:attachments={(ev) => {
|
||||
if (ev.detail.size > 0) attachments = ev.detail.values
|
||||
else if (ev.detail.size === 0 && ev.detail.values != null) {
|
||||
attachments.clear()
|
||||
attachments = attachments
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<svelte:fragment slot="pool">
|
||||
<AssigneeEditor
|
||||
object={{ ...object, space }}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
on:change={({ detail }) => (object.assignee = detail)}
|
||||
/>
|
||||
<StatusEditor bind:value={object.status} {object} kind="regular" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="attachments">
|
||||
{#if attachments.size > 0}
|
||||
{#each Array.from(attachments.values()) as attachment}
|
||||
<AttachmentPresenter
|
||||
value={attachment}
|
||||
showPreview
|
||||
removable
|
||||
on:remove={(result) => {
|
||||
if (result.detail !== undefined) descriptionBox.removeAttachmentById(result.detail._id)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
icon={IconAttachment}
|
||||
size="large"
|
||||
on:click={() => {
|
||||
descriptionBox.handleAttach()
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
@ -0,0 +1,103 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { type Class, type Ref } from '@hcengineering/core'
|
||||
import { TestCase } from '@hcengineering/test-management'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import testManagement from '../../plugin'
|
||||
|
||||
export let _id: Ref<TestCase>
|
||||
export let _class: Ref<Class<TestCase>>
|
||||
|
||||
let object: TestCase | undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let oldLabel: string | undefined = ''
|
||||
let rawLabel: string | undefined = ''
|
||||
let descriptionBox: AttachmentStyleBoxCollabEditor
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: _id !== undefined &&
|
||||
_class !== undefined &&
|
||||
query.query(_class, { _id }, async (result) => {
|
||||
;[object] = result
|
||||
})
|
||||
|
||||
async function change<K extends keyof TestCase> (field: K, value: TestCase[K]) {
|
||||
if (object !== undefined) {
|
||||
await client.update(object, { [field]: value })
|
||||
}
|
||||
}
|
||||
|
||||
let content: HTMLElement
|
||||
|
||||
$: if (oldLabel !== object?.name) {
|
||||
oldLabel = object?.name
|
||||
rawLabel = object?.name
|
||||
}
|
||||
|
||||
$: descriptionKey = hierarchy.getAttribute(testManagement.class.TestCase, 'description')
|
||||
|
||||
onMount(() => dispatch('open', { ignoreKeys: [] }))
|
||||
</script>
|
||||
|
||||
{#if object}
|
||||
<ActionContext context={{ mode: 'editor' }} />
|
||||
<Panel
|
||||
{object}
|
||||
title={object.name}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
adaptive={'default'}
|
||||
on:open
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<EditBox
|
||||
bind:value={rawLabel}
|
||||
placeholder={testManagement.string.NamePlaceholder}
|
||||
kind="large-style"
|
||||
on:blur={async () => {
|
||||
const trimmedLabel = rawLabel?.trim()
|
||||
|
||||
if (trimmedLabel?.length === 0) {
|
||||
rawLabel = oldLabel
|
||||
} else if (trimmedLabel !== object?.name) {
|
||||
await change('name', trimmedLabel ?? '')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<AttachmentStyleBoxCollabEditor
|
||||
focusIndex={30}
|
||||
{object}
|
||||
key={{ key: 'description', attr: descriptionKey }}
|
||||
bind:this={descriptionBox}
|
||||
identifier={object?._id}
|
||||
placeholder={testManagement.string.DescriptionPlaceholder}
|
||||
boundary={content}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
{/if}
|
@ -0,0 +1,45 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, showPopup } from '@hcengineering/ui'
|
||||
import testManagement from '../../plugin'
|
||||
import CreateTestCase from './CreateTestCase.svelte'
|
||||
import { openDoc } from '@hcengineering/view-resources'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function newTestCase (): Promise<void> {
|
||||
showPopup(CreateTestCase, {}, 'top', async (id) => {
|
||||
if (id != null) {
|
||||
const doc = await client.findOne(testManagement.class.TestCase, { _id: id })
|
||||
if (doc !== undefined) {
|
||||
void openDoc(client.getHierarchy(), doc)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={testManagement.string.CreateTestCase}
|
||||
kind={'primary'}
|
||||
justify={'left'}
|
||||
width="100%"
|
||||
on:click={newTestCase}
|
||||
/>
|
||||
</div>
|
@ -0,0 +1,105 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Data } from '@hcengineering/core'
|
||||
import { TestCase } from '@hcengineering/test-management'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
Button,
|
||||
ButtonKind,
|
||||
ButtonSize,
|
||||
Icon,
|
||||
SelectPopup,
|
||||
eventToHTMLElement,
|
||||
showPopup,
|
||||
Label
|
||||
} from '@hcengineering/ui'
|
||||
import { defaultTestCaseStatuses, testCaseStatusAssets } from '../../types'
|
||||
import testManagement from '../../plugin'
|
||||
|
||||
export let value: TestCase['status'] | undefined
|
||||
export let object: TestCase | Data<TestCase>
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = undefined
|
||||
export let disabled = false
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let accent: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
function handlePopupOpen (event: MouseEvent) {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{ value: itemsInfo, placeholder: testManagement.string.SetStatus },
|
||||
eventToHTMLElement(event),
|
||||
changeStatus
|
||||
)
|
||||
}
|
||||
|
||||
async function changeStatus (newStatus: TestCase['status'] | null | undefined) {
|
||||
if (disabled || newStatus == null || value === newStatus) {
|
||||
return
|
||||
}
|
||||
|
||||
value = newStatus
|
||||
dispatch('change', value)
|
||||
|
||||
if ('_id' in object) {
|
||||
await client.update(object, { status: newStatus })
|
||||
}
|
||||
}
|
||||
|
||||
$: itemsInfo = defaultTestCaseStatuses.map((status) => ({
|
||||
id: status,
|
||||
isSelected: value === status,
|
||||
...testCaseStatusAssets[status]
|
||||
}))
|
||||
|
||||
$: icon = value === undefined ? testManagement.icon.StatusDraft : testCaseStatusAssets[value].icon
|
||||
$: label = value === undefined ? testManagement.string.StatusDraft : testCaseStatusAssets[value].label
|
||||
</script>
|
||||
|
||||
{#if kind === 'list'}
|
||||
<button
|
||||
class="flex-no-shrink clear-mins cursor-pointer content-pointer-events-none"
|
||||
{disabled}
|
||||
on:click={handlePopupOpen}
|
||||
>
|
||||
<Icon {icon} {size} />
|
||||
</button>
|
||||
{:else if kind === 'list-header'}
|
||||
<div class="flex-row-center pl-0-5">
|
||||
{#if shouldShowAvatar}
|
||||
<Icon {icon} {size} />
|
||||
{/if}
|
||||
<span class="overflow-label" class:ml-1-5={shouldShowAvatar} class:fs-bold={accent}><Label {label} /></span>
|
||||
</div>
|
||||
{:else}
|
||||
<Button
|
||||
{label}
|
||||
{kind}
|
||||
{icon}
|
||||
{justify}
|
||||
{size}
|
||||
{width}
|
||||
{disabled}
|
||||
showTooltip={{ label: testManagement.string.SetStatus }}
|
||||
on:click={handlePopupOpen}
|
||||
/>
|
||||
{/if}
|
@ -0,0 +1,43 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { TestCase } from '@hcengineering/test-management'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: TestCase | undefined
|
||||
export let inline: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let noUnderline: boolean = false
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
|
||||
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}>
|
||||
{value.name}
|
||||
</span>
|
||||
</div>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
@ -0,0 +1,44 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TestCase, TestCaseStatus } from '@hcengineering/test-management'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
|
||||
export let value: TestCaseStatus
|
||||
export let object: TestCase
|
||||
export let onChange: ((value: TestCaseStatus) => void) | undefined = undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = '100%'
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let accent: boolean = false
|
||||
|
||||
$: disabled = onChange === undefined
|
||||
</script>
|
||||
|
||||
<StatusEditor
|
||||
{value}
|
||||
{object}
|
||||
{kind}
|
||||
{size}
|
||||
{width}
|
||||
{justify}
|
||||
{disabled}
|
||||
{accent}
|
||||
{shouldShowAvatar}
|
||||
on:change={({ detail }) => onChange?.(detail)}
|
||||
/>
|
@ -0,0 +1,135 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import core, { Data, Ref, generateId, makeCollaborativeDoc } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, SpaceSelector, getClient } from '@hcengineering/presentation'
|
||||
import { TestRun, TestProject } from '@hcengineering/test-management'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { EmptyMarkup } from '@hcengineering/text'
|
||||
|
||||
import testManagement from '../../plugin'
|
||||
import ProjectPresenter from '../project/ProjectSpacePresenter.svelte'
|
||||
|
||||
export let space: Ref<TestProject>
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
const id: Ref<TestRun> = generateId()
|
||||
|
||||
const object: Data<TestRun> = {
|
||||
name: '' as IntlString,
|
||||
description: makeCollaborativeDoc(id, 'description')
|
||||
}
|
||||
|
||||
let _space = space
|
||||
|
||||
let description = EmptyMarkup
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
|
||||
async function onSave () {
|
||||
await client.createDoc(testManagement.class.TestRun, _space, object)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={testManagement.string.CreateTestRun}
|
||||
okAction={onSave}
|
||||
canSave={object.name !== ''}
|
||||
okLabel={testManagement.string.CreateTestRun}
|
||||
gap={'gapV-4'}
|
||||
on:close={() => dispatch('close')}
|
||||
on:changeContent
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector
|
||||
_class={testManagement.class.TestProject}
|
||||
label={testManagement.string.TestProject}
|
||||
bind:space={_space}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
component={ProjectPresenter}
|
||||
defaultIcon={testManagement.icon.Home}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<EditBox
|
||||
bind:value={object.name}
|
||||
placeholder={testManagement.string.TestRunNamePlaceholder}
|
||||
kind={'large-style'}
|
||||
autoFocus
|
||||
/>
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
objectId={id}
|
||||
_class={testManagement.class.TestRun}
|
||||
space={_space}
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
bind:content={description}
|
||||
placeholder={core.string.Description}
|
||||
kind="indented"
|
||||
isScrollable={false}
|
||||
enableBackReferences={true}
|
||||
enableAttachments={false}
|
||||
on:attachments={(ev) => {
|
||||
if (ev.detail.size > 0) attachments = ev.detail.values
|
||||
else if (ev.detail.size === 0 && ev.detail.values != null) {
|
||||
attachments.clear()
|
||||
attachments = attachments
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
<ObjectBox
|
||||
_class={testManagement.class.TestSuite}
|
||||
value={null}
|
||||
docQuery={{
|
||||
space: _space
|
||||
}}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
label={testManagement.string.SelectTestSuites}
|
||||
icon={testManagement.icon.TestSuite}
|
||||
searchField={'title'}
|
||||
allowDeselect={true}
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
focusIndex={20000}
|
||||
/>
|
||||
<ObjectBox
|
||||
_class={testManagement.class.TestCase}
|
||||
value={null}
|
||||
docQuery={{
|
||||
space: _space
|
||||
}}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
label={testManagement.string.SelectTestCases}
|
||||
icon={testManagement.icon.TestCase}
|
||||
searchField={'title'}
|
||||
allowDeselect={true}
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
focusIndex={20000}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader"></div>
|
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader"></div>
|
@ -0,0 +1,98 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Data, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, SpaceSelector, getClient } from '@hcengineering/presentation'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor-resources'
|
||||
import { TestSuite, TestProject } from '@hcengineering/test-management'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import testManagement from '../../plugin'
|
||||
import ProjectPresenter from '../project/ProjectSpacePresenter.svelte'
|
||||
|
||||
export let space: Ref<TestProject>
|
||||
export let parentId: Ref<TestSuite> = testManagement.ids.NoParent
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
const object: Data<TestSuite> = {
|
||||
name: '' as IntlString,
|
||||
description: '',
|
||||
parent: parentId
|
||||
}
|
||||
|
||||
let _space = space
|
||||
|
||||
function handleTestSuiteChange (evt: CustomEvent<Ref<TestSuite>>): void {
|
||||
object.parent = evt.detail
|
||||
}
|
||||
|
||||
async function onSave () {
|
||||
await client.createDoc(testManagement.class.TestSuite, _space, object)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={testManagement.string.CreateTestSuite}
|
||||
okAction={onSave}
|
||||
canSave={object.name !== ''}
|
||||
okLabel={testManagement.string.CreateTestSuite}
|
||||
gap={'gapV-4'}
|
||||
on:close={() => dispatch('close')}
|
||||
on:changeContent
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector
|
||||
_class={testManagement.class.TestProject}
|
||||
label={testManagement.string.TestProject}
|
||||
bind:space={_space}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
component={ProjectPresenter}
|
||||
defaultIcon={testManagement.icon.Home}
|
||||
/>
|
||||
<ObjectBox
|
||||
_class={testManagement.class.TestSuite}
|
||||
value={parentId}
|
||||
docQuery={{
|
||||
space: _space
|
||||
}}
|
||||
on:change={handleTestSuiteChange}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
label={testManagement.string.NoTestSuite}
|
||||
icon={testManagement.icon.TestSuite}
|
||||
searchField={'title'}
|
||||
allowDeselect={true}
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
focusIndex={20000}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<EditBox
|
||||
bind:value={object.name}
|
||||
placeholder={testManagement.string.NamePlaceholder}
|
||||
kind={'large-style'}
|
||||
autoFocus
|
||||
/>
|
||||
<StyledTextArea
|
||||
bind:content={object.description}
|
||||
placeholder={testManagement.string.DescriptionPlaceholder}
|
||||
kind={'emphasized'}
|
||||
showButtons={false}
|
||||
/>
|
||||
</Card>
|
@ -0,0 +1,100 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { type Class, type Ref } from '@hcengineering/core'
|
||||
import { TestSuite } from '@hcengineering/test-management'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor-resources'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import TestCasesList from './TestCasesList.svelte'
|
||||
import testManagement from '../../plugin'
|
||||
|
||||
export let _id: Ref<TestSuite>
|
||||
export let _class: Ref<Class<TestSuite>>
|
||||
|
||||
let object: TestSuite
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let oldLabel: string | undefined = ''
|
||||
let rawLabel: string | undefined = ''
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: _id !== undefined &&
|
||||
_class !== undefined &&
|
||||
query.query(_class, { _id }, async (result) => {
|
||||
;[object] = result
|
||||
})
|
||||
|
||||
async function change<K extends keyof TestSuite> (field: K, value: TestSuite[K]) {
|
||||
if (object !== undefined) {
|
||||
await client.update(object, { [field]: value })
|
||||
}
|
||||
}
|
||||
|
||||
$: if (oldLabel !== object?.name) {
|
||||
oldLabel = object?.name
|
||||
rawLabel = object?.name
|
||||
}
|
||||
|
||||
onMount(() => dispatch('open', { ignoreKeys: [] }))
|
||||
</script>
|
||||
|
||||
{#if object}
|
||||
<ActionContext context={{ mode: 'editor' }} />
|
||||
<Panel
|
||||
{object}
|
||||
title={object.name}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
adaptive={'default'}
|
||||
on:open
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<EditBox
|
||||
bind:value={rawLabel}
|
||||
placeholder={testManagement.string.NamePlaceholder}
|
||||
kind="large-style"
|
||||
on:blur={async () => {
|
||||
const trimmedLabel = rawLabel?.trim()
|
||||
|
||||
if (trimmedLabel?.length === 0) {
|
||||
rawLabel = oldLabel
|
||||
} else if (trimmedLabel !== object?.name) {
|
||||
await change('name', trimmedLabel ?? '')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<StyledTextArea
|
||||
bind:content={object.description}
|
||||
placeholder={testManagement.string.DescriptionPlaceholder}
|
||||
kind={'emphasized'}
|
||||
showButtons={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<TestCasesList objectId={object._id} />
|
||||
</div>
|
||||
</Panel>
|
||||
{/if}
|
@ -0,0 +1,86 @@
|
||||
<!--
|
||||
// Copyright © 2024 Anticrm Platform Contributors.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { testManagementId, TestSuite } from '@hcengineering/test-management'
|
||||
import { Button, Icon, IconAdd, Label, Loading, Scroller, showPopup, SectionEmpty } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { NavLink, Table, ViewletsSettingButton } from '@hcengineering/view-resources'
|
||||
import testManagement from '../../plugin'
|
||||
import CreateTestCase from '../test-case/CreateTestCase.svelte'
|
||||
import FileDuo from '../icons/FileDuo.svelte'
|
||||
|
||||
export let objectId: Ref<TestSuite>
|
||||
let testCases: number
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(testManagement.class.TestCase, { suite: objectId }, (res) => {
|
||||
testCases = res.length
|
||||
})
|
||||
|
||||
const createTestCase = (ev: MouseEvent): void => {
|
||||
showPopup(CreateTestCase, { testSuiteId: objectId }, ev.target as HTMLElement)
|
||||
}
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let preference: ViewletPreference | undefined
|
||||
let loading = true
|
||||
</script>
|
||||
|
||||
<div class="antiSection max-h-125 clear-mins">
|
||||
<div class="antiSection-header">
|
||||
<div class="antiSection-header__icon">
|
||||
<Icon icon={testManagement.icon.TestCase} size={'small'} />
|
||||
</div>
|
||||
<span class="antiSection-header__title">
|
||||
<NavLink app={testManagementId} space={objectId}>
|
||||
<Label label={testManagement.string.TestCases} />
|
||||
</NavLink>
|
||||
</span>
|
||||
<div class="flex-row-center gap-2 reverse">
|
||||
<ViewletsSettingButton
|
||||
viewletQuery={{ _id: testManagement.viewlet.SuiteTestCases }}
|
||||
kind={'tertiary'}
|
||||
bind:viewlet
|
||||
bind:preference
|
||||
bind:loading
|
||||
/>
|
||||
<Button id="appls.add" icon={IconAdd} kind={'ghost'} on:click={createTestCase} />
|
||||
</div>
|
||||
</div>
|
||||
{#if testCases > 0}
|
||||
{#if viewlet !== undefined && !loading}
|
||||
<Scroller horizontal>
|
||||
<Table
|
||||
_class={testManagement.class.TestCase}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
query={{ suite: objectId }}
|
||||
loadingProps={{ length: testCases }}
|
||||
/>
|
||||
</Scroller>
|
||||
{:else}
|
||||
<Loading />
|
||||
{/if}
|
||||
{:else}
|
||||
<SectionEmpty icon={FileDuo} label={testManagement.string.NoTestCases}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<span class="over-underline content-color" on:click={createTestCase}>
|
||||
<Label label={testManagement.string.CreateTestCase} />
|
||||
</span>
|
||||
</SectionEmpty>
|
||||
{/if}
|
||||
</div>
|
@ -0,0 +1,43 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { TestSuite } from '@hcengineering/test-management'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink, ObjectMention } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: TestSuite | undefined
|
||||
export let inline: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let noUnderline: boolean = false
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
{#if inline}
|
||||
<ObjectMention object={value} {disabled} {accent} {noUnderline} />
|
||||
{:else}
|
||||
<DocNavLink object={value} {disabled} {accent} {noUnderline}>
|
||||
<div class="flex-presenter" use:tooltip={{ label: getEmbeddedLabel(value.name) }}>
|
||||
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}>
|
||||
{value.name}
|
||||
</span>
|
||||
</div>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
{/if}
|
@ -0,0 +1,39 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { TestSuite } from '@hcengineering/test-management'
|
||||
|
||||
import testManagement from '../../plugin'
|
||||
import TestSuitePresenter from './TestSuitePresenter.svelte'
|
||||
|
||||
export let value: Ref<TestSuite> | undefined
|
||||
export let _class: Ref<Class<TestSuite>> = testManagement.class.TestSuite
|
||||
export let inline: boolean = false
|
||||
export let accent: boolean = false
|
||||
|
||||
let project: TestSuite | undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: value !== undefined &&
|
||||
query.query(_class, { _id: value }, (res) => {
|
||||
;[project] = res
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<TestSuitePresenter value={project} {inline} {accent} />
|
||||
{/if}
|
62
plugins/test-management-resources/src/index.ts
Normal file
62
plugins/test-management-resources/src/index.ts
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import NewTestCaseHeader from './components/test-case/NewTestCaseHeader.svelte'
|
||||
import CreateProject from './components/project/CreateProject.svelte'
|
||||
import ProjectSpacePresenter from './components/project/ProjectSpacePresenter.svelte'
|
||||
import CreateTestSuite from './components/test-suite/CreateTestSuite.svelte'
|
||||
import EditTestSuite from './components/test-suite/EditTestSuite.svelte'
|
||||
import TestSuitePresenter from './components/test-suite/TestSuitePresenter.svelte'
|
||||
import TestSuiteRefPresenter from './components/test-suite/TestSuiteRefPresenter.svelte'
|
||||
import EditTestCase from './components/test-case/EditTestCase.svelte'
|
||||
import TestCasePresenter from './components/test-case/TestCasePresenter.svelte'
|
||||
import CreateTestCase from './components/test-case/CreateTestCase.svelte'
|
||||
import CreateTestRun from './components/test-run/CreateTestRun.svelte'
|
||||
import TestCaseStatusPresenter from './components/test-case/TestCaseStatusPresenter.svelte'
|
||||
import EditTestRun from './components/test-run/EditTestRun.svelte'
|
||||
import TestRunPresenter from './components/test-run/TestRunPresenter.svelte'
|
||||
|
||||
import { CreateChildTestSuiteAction, EditTestSuiteAction } from './utils'
|
||||
import { resolveLocation, getTestSuiteLink } from './navigation'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
NewTestCaseHeader,
|
||||
CreateProject,
|
||||
ProjectSpacePresenter,
|
||||
CreateTestSuite,
|
||||
EditTestSuite,
|
||||
TestSuitePresenter,
|
||||
EditTestCase,
|
||||
TestCasePresenter,
|
||||
CreateTestRun,
|
||||
CreateTestCase,
|
||||
TestCaseStatusPresenter,
|
||||
EditTestRun,
|
||||
TestRunPresenter,
|
||||
TestSuiteRefPresenter
|
||||
},
|
||||
function: {
|
||||
GetTestSuiteLink: getTestSuiteLink
|
||||
},
|
||||
resolver: {
|
||||
Location: resolveLocation
|
||||
},
|
||||
actionImpl: {
|
||||
CreateChildTestSuite: CreateChildTestSuiteAction,
|
||||
EditTestSuite: EditTestSuiteAction
|
||||
}
|
||||
})
|
84
plugins/test-management-resources/src/navigation.ts
Normal file
84
plugins/test-management-resources/src/navigation.ts
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import testManagement, { testManagementId, type TestSuite, type TestProject } from '@hcengineering/test-management'
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { getCurrentResolvedLocation, getPanelURI, type Location, type ResolvedLocation } from '@hcengineering/ui'
|
||||
import view, { type ObjectPanel } from '@hcengineering/view'
|
||||
import { accessDeniedStore } from '@hcengineering/view-resources'
|
||||
|
||||
export function getPanelFragment<T extends Doc> (object: Pick<T, '_class' | '_id'>): string {
|
||||
const hierarchy = getClient().getHierarchy()
|
||||
const objectPanelMixin = hierarchy.classHierarchyMixin<Doc, ObjectPanel>(object._class, view.mixin.ObjectPanel)
|
||||
const component = objectPanelMixin?.component ?? view.component.EditDoc
|
||||
return getPanelURI(component, object._id, object._class, 'content')
|
||||
}
|
||||
|
||||
async function generateProjectLocation (
|
||||
loc: Location,
|
||||
project: Ref<TestProject>
|
||||
): Promise<ResolvedLocation | undefined> {
|
||||
const client = getClient()
|
||||
|
||||
const doc = await client.findOne(testManagement.class.TestProject, { _id: project })
|
||||
if (doc === undefined) {
|
||||
accessDeniedStore.set(true)
|
||||
console.error(`Could not find test project ${project}.`)
|
||||
return undefined
|
||||
}
|
||||
|
||||
const appComponent = loc.path[0] ?? ''
|
||||
const workspace = loc.path[1] ?? ''
|
||||
|
||||
return {
|
||||
loc: {
|
||||
...loc,
|
||||
path: [appComponent, workspace, testManagementId, project, 'library']
|
||||
},
|
||||
defaultLocation: {
|
||||
...loc,
|
||||
path: [appComponent, workspace, testManagementId, project, 'library']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getTestSuiteLink (testSuite: Ref<TestSuite>): Location {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.query =
|
||||
testSuite === undefined
|
||||
? undefined
|
||||
: {
|
||||
attachedTo: testSuite
|
||||
}
|
||||
|
||||
return loc
|
||||
}
|
||||
|
||||
export function getTestSuiteIdFromFragment (fragment: string): Ref<TestSuite> | undefined {
|
||||
const props = decodeURIComponent(fragment).split('|')
|
||||
|
||||
return props[6] != null ? (props[6] as Ref<TestSuite>) : undefined
|
||||
}
|
||||
|
||||
export async function resolveLocation (loc: Location): Promise<ResolvedLocation | undefined> {
|
||||
if (loc.path[2] !== testManagementId) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const projectId = loc.path[3] as Ref<TestProject>
|
||||
if (projectId !== undefined) {
|
||||
return await generateProjectLocation(loc, projectId)
|
||||
}
|
||||
return undefined
|
||||
}
|
25
plugins/test-management-resources/src/plugin.ts
Normal file
25
plugins/test-management-resources/src/plugin.ts
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import testManagement, { testManagementId } from '@hcengineering/test-management'
|
||||
|
||||
export default mergeIds(testManagementId, testManagement, {
|
||||
string: {
|
||||
Add: '' as IntlString,
|
||||
Remove: '' as IntlString
|
||||
}
|
||||
})
|
41
plugins/test-management-resources/src/types.ts
Normal file
41
plugins/test-management-resources/src/types.ts
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import { type Asset, type IntlString } from '@hcengineering/platform'
|
||||
|
||||
import testManagement, { TestCaseStatus } from '@hcengineering/test-management'
|
||||
|
||||
/** @public */
|
||||
export const defaultTestCaseStatuses = [
|
||||
TestCaseStatus.Draft,
|
||||
TestCaseStatus.ReadyForReview,
|
||||
TestCaseStatus.FixReviewComments,
|
||||
TestCaseStatus.Approved,
|
||||
TestCaseStatus.Rejected
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export const testCaseStatusAssets: Record<TestCaseStatus, { icon: Asset, label: IntlString }> = {
|
||||
[TestCaseStatus.Draft]: { icon: testManagement.icon.StatusDraft, label: testManagement.string.StatusDraft },
|
||||
[TestCaseStatus.ReadyForReview]: {
|
||||
icon: testManagement.icon.StatusReview,
|
||||
label: testManagement.string.StatusReview
|
||||
},
|
||||
[TestCaseStatus.FixReviewComments]: {
|
||||
icon: testManagement.icon.StatusReviewComments,
|
||||
label: testManagement.string.StatusReviewComments
|
||||
},
|
||||
[TestCaseStatus.Approved]: { icon: testManagement.icon.StatusApproved, label: testManagement.string.StatusApproved },
|
||||
[TestCaseStatus.Rejected]: { icon: testManagement.icon.StatusRejected, label: testManagement.string.StatusRejected }
|
||||
}
|
71
plugins/test-management-resources/src/utils.ts
Normal file
71
plugins/test-management-resources/src/utils.ts
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Contact } from '@hcengineering/contact'
|
||||
import core, { type Doc, type Ref, type TxCollectionCUD, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { type TestProject, type TestCase, type TestSuite } from '@hcengineering/test-management'
|
||||
|
||||
import CreateTestSuiteComponent from './components/test-suite/CreateTestSuite.svelte'
|
||||
import EditTestSuiteComponent from './components/test-suite/EditTestSuite.svelte'
|
||||
|
||||
export async function getPreviousAssignees (objectId: Ref<Doc> | undefined): Promise<Array<Ref<Contact>>> {
|
||||
if (objectId === undefined) {
|
||||
return []
|
||||
}
|
||||
const client = getClient()
|
||||
const createTx = (
|
||||
await client.findAll<TxCollectionCUD<TestCase, TestCase>>(core.class.TxCollectionCUD, {
|
||||
'tx.objectId': objectId,
|
||||
'tx._class': core.class.TxCreateDoc
|
||||
})
|
||||
)[0]
|
||||
const updateTxes = await client.findAll<TxCollectionCUD<TestCase, TestCase>>(
|
||||
core.class.TxCollectionCUD,
|
||||
{ 'tx.objectId': objectId, 'tx._class': core.class.TxUpdateDoc, 'tx.operations.assignee': { $exists: true } },
|
||||
{ sort: { modifiedOn: -1 } }
|
||||
)
|
||||
const set = new Set<Ref<Contact>>()
|
||||
const createAssignee = (createTx?.tx as TxCreateDoc<TestCase>)?.attributes?.assignee
|
||||
for (const tx of updateTxes) {
|
||||
const assignee = (tx.tx as TxUpdateDoc<TestCase>).operations.assignee
|
||||
if (assignee == null) continue
|
||||
set.add(assignee)
|
||||
}
|
||||
if (createAssignee != null) {
|
||||
set.add(createAssignee)
|
||||
}
|
||||
return Array.from(set)
|
||||
}
|
||||
|
||||
export async function showCreateTestSuitePopup (
|
||||
space: Ref<TestProject> | undefined,
|
||||
parentId: Ref<TestSuite>
|
||||
): Promise<void> {
|
||||
showPopup(CreateTestSuiteComponent, { space, parentId }, 'top')
|
||||
}
|
||||
|
||||
export async function showEditTestSuitePopup (suite: Ref<TestSuite>): Promise<void> {
|
||||
showPopup(EditTestSuiteComponent, { _id: suite }, 'top')
|
||||
}
|
||||
|
||||
export async function CreateChildTestSuiteAction (doc: TestSuite): Promise<void> {
|
||||
await showCreateTestSuitePopup(doc.space, doc._id)
|
||||
}
|
||||
|
||||
export async function EditTestSuiteAction (doc: TestSuite): Promise<void> {
|
||||
await showEditTestSuitePopup(doc._id)
|
||||
}
|
5
plugins/test-management-resources/svelte.config.js
Normal file
5
plugins/test-management-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
const sveltePreprocess = require('svelte-preprocess')
|
||||
|
||||
module.exports = {
|
||||
preprocess: sveltePreprocess()
|
||||
};
|
9
plugins/test-management-resources/tsconfig.json
Normal file
9
plugins/test-management-resources/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types"
|
||||
}
|
||||
}
|
7
plugins/test-management/.eslintrc.js
Normal file
7
plugins/test-management/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
plugins/test-management/.npmignore
Normal file
4
plugins/test-management/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
4
plugins/test-management/config/rig.json
Normal file
4
plugins/test-management/config/rig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
7
plugins/test-management/jest.config.js
Normal file
7
plugins/test-management/jest.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
56
plugins/test-management/package.json
Normal file
56
plugins/test-management/package.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@hcengineering/test-management",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*",
|
||||
"types/**/*",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/platform": "^0.6.11",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/chunter": "^0.6.20",
|
||||
"@hcengineering/attachment": "^0.6.14",
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/tags": "^0.6.16",
|
||||
"@hcengineering/preference": "^0.6.13",
|
||||
"lexorank": "~1.0.4"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
"publishConfig": {
|
||||
"registry": "https://npm.pkg.github.com"
|
||||
}
|
||||
}
|
21
plugins/test-management/src/index.ts
Normal file
21
plugins/test-management/src/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { testManagementId, testManagementPlugin } from './plugin'
|
||||
|
||||
export * from './types'
|
||||
export { testManagementId }
|
||||
|
||||
export default testManagementPlugin
|
220
plugins/test-management/src/plugin.ts
Normal file
220
plugins/test-management/src/plugin.ts
Normal file
@ -0,0 +1,220 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import {
|
||||
Mixin,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Ref,
|
||||
Type,
|
||||
type Status,
|
||||
type SpaceTypeDescriptor,
|
||||
type SpaceType
|
||||
} from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
|
||||
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { type AnyComponent, type Location, type ResolvedLocation } from '@hcengineering/ui'
|
||||
|
||||
import { ActionCategory, Viewlet } from '@hcengineering/view'
|
||||
import {
|
||||
TestSuite,
|
||||
TestCase,
|
||||
TestProject,
|
||||
TestCaseType,
|
||||
TestCasePriority,
|
||||
TestCaseStatus,
|
||||
TestRun,
|
||||
TestRunItem,
|
||||
TestRunResult
|
||||
} from './types'
|
||||
|
||||
/** @public */
|
||||
export const testManagementId = 'testManagement' as Plugin
|
||||
|
||||
/** @public */
|
||||
export const testManagementPlugin = plugin(testManagementId, {
|
||||
app: {
|
||||
TestManagement: '' as Ref<Doc>
|
||||
},
|
||||
icon: {
|
||||
TestManagement: '' as Asset,
|
||||
TestManagementVersion: '' as Asset,
|
||||
TestManagementApplication: '' as Asset,
|
||||
TestCase: '' as Asset,
|
||||
TestCases: '' as Asset,
|
||||
Home: '' as Asset,
|
||||
Estimation: '' as Asset,
|
||||
TestSuite: '' as Asset,
|
||||
TestProject: '' as Asset,
|
||||
TestSuites: '' as Asset,
|
||||
TestRuns: '' as Asset,
|
||||
RedCircle: '' as Asset,
|
||||
StatusDraft: '' as Asset,
|
||||
StatusReview: '' as Asset,
|
||||
StatusReviewComments: '' as Asset,
|
||||
StatusApproved: '' as Asset,
|
||||
StatusRejected: '' as Asset,
|
||||
Document: '' as Asset,
|
||||
TestLibrary: '' as Asset
|
||||
},
|
||||
class: {
|
||||
TestCase: '' as Ref<Class<TestCase>>,
|
||||
TestSuite: '' as Ref<Class<TestSuite>>,
|
||||
TestProject: '' as Ref<Class<TestProject>>,
|
||||
TypeTestCaseType: '' as Ref<Class<Type<TestCaseType>>>,
|
||||
TypeTestCasePriority: '' as Ref<Class<Type<TestCasePriority>>>,
|
||||
TypeTestCaseStatus: '' as Ref<Class<Type<TestCaseStatus>>>,
|
||||
TestRun: '' as Ref<Class<TestRun>>,
|
||||
TestRunItem: '' as Ref<Class<TestRunItem>>,
|
||||
TypeTestRunResult: '' as Ref<Class<Type<TestRunResult>>>
|
||||
},
|
||||
descriptors: {
|
||||
ProjectType: '' as Ref<SpaceTypeDescriptor>
|
||||
},
|
||||
mixin: {
|
||||
TestCaseTypeData: '' as Ref<Mixin<TestCase>>,
|
||||
TestProject: '' as Ref<Mixin<TestProject>>,
|
||||
DefaultProjectTypeData: '' as Ref<Mixin<TestProject>>
|
||||
},
|
||||
string: {
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString,
|
||||
TestCaseType: '' as IntlString,
|
||||
TestCasePriority: '' as IntlString,
|
||||
TestCaseStatus: '' as IntlString,
|
||||
TestSuite: '' as IntlString,
|
||||
SuiteName: '' as IntlString,
|
||||
SuiteDescription: '' as IntlString,
|
||||
Suite: '' as IntlString,
|
||||
TestName: '' as IntlString,
|
||||
TestDescription: '' as IntlString,
|
||||
TestType: '' as IntlString,
|
||||
TestPriority: '' as IntlString,
|
||||
TestStatus: '' as IntlString,
|
||||
TestEstimatedTime: '' as IntlString,
|
||||
TestPreconditions: '' as IntlString,
|
||||
TestSteps: '' as IntlString,
|
||||
TestAssignee: '' as IntlString,
|
||||
TestCase: '' as IntlString,
|
||||
TestProject: '' as IntlString,
|
||||
TestManagementApplication: '' as IntlString,
|
||||
AllTestCases: '' as IntlString,
|
||||
AllProjects: '' as IntlString,
|
||||
Projects: '' as IntlString,
|
||||
CreateProject: '' as IntlString,
|
||||
TestCases: '' as IntlString,
|
||||
TestManagementDescription: '' as IntlString,
|
||||
CreateTestCase: '' as IntlString,
|
||||
FullDescription: '' as IntlString,
|
||||
EditProject: '' as IntlString,
|
||||
ProjectName: '' as IntlString,
|
||||
ProjectType: '' as IntlString,
|
||||
Members: '' as IntlString,
|
||||
RoleLabel: '' as IntlString,
|
||||
ProjectMembers: '' as IntlString,
|
||||
ManageProjectStatuses: '' as IntlString,
|
||||
TestSuites: '' as IntlString,
|
||||
CreateTestSuite: '' as IntlString,
|
||||
NamePlaceholder: '' as IntlString,
|
||||
DescriptionPlaceholder: '' as IntlString,
|
||||
TestRuns: '' as IntlString,
|
||||
NewTestRun: '' as IntlString,
|
||||
TestRun: '' as IntlString,
|
||||
TestNamePlaceholder: '' as IntlString,
|
||||
ChooseIcon: '' as IntlString,
|
||||
NoTestSuite: '' as IntlString,
|
||||
StatusDraft: '' as IntlString,
|
||||
StatusReview: '' as IntlString,
|
||||
StatusReviewComments: '' as IntlString,
|
||||
StatusApproved: '' as IntlString,
|
||||
StatusRejected: '' as IntlString,
|
||||
SetStatus: '' as IntlString,
|
||||
Assignee: '' as IntlString,
|
||||
Unassigned: '' as IntlString,
|
||||
AssignTo: '' as IntlString,
|
||||
AssignedTo: '' as IntlString,
|
||||
PreviousAssigned: '' as IntlString,
|
||||
TestRunName: '' as IntlString,
|
||||
NoTestCases: '' as IntlString,
|
||||
DueDate: '' as IntlString,
|
||||
TestRunItems: '' as IntlString,
|
||||
TestRunResult: '' as IntlString,
|
||||
TestRunItem: '' as IntlString,
|
||||
TestRunNamePlaceholder: '' as IntlString,
|
||||
SelectTestSuites: '' as IntlString,
|
||||
SelectTestCases: '' as IntlString,
|
||||
CreateTestRun: '' as IntlString,
|
||||
TestLibrary: '' as IntlString
|
||||
},
|
||||
category: {
|
||||
TestManagement: '' as Ref<ActionCategory>
|
||||
},
|
||||
component: {
|
||||
TestCaseSearchIcon: '' as AnyComponent,
|
||||
TestCases: '' as AnyComponent,
|
||||
CreateProject: '' as AnyComponent,
|
||||
NewTestCaseHeader: '' as AnyComponent,
|
||||
TestCaseStatusIcon: '' as AnyComponent,
|
||||
PriorityIconPresenter: '' as AnyComponent,
|
||||
TestCaseStatusPresenter: '' as AnyComponent,
|
||||
TestSuites: '' as AnyComponent,
|
||||
CreateTestSuite: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
NoParent: '' as Ref<TestSuite>,
|
||||
TestCaseUpdatedActivityViewlet: '' as Ref<TestCase>
|
||||
},
|
||||
spaceType: {
|
||||
TestCaseType: '' as Ref<SpaceType>,
|
||||
DefaultProject: '' as Ref<SpaceType>
|
||||
},
|
||||
spaceTypeDescriptor: {
|
||||
TestCaseType: '' as Ref<SpaceTypeDescriptor>
|
||||
},
|
||||
template: {
|
||||
DefaultProject: '' as Ref<SpaceTypeDescriptor>
|
||||
},
|
||||
space: {
|
||||
DefaultProject: '' as Ref<TestProject>
|
||||
},
|
||||
viewlet: {
|
||||
TableTestCase: '' as Ref<Viewlet>,
|
||||
TableTestSuites: '' as Ref<Viewlet>,
|
||||
TableTestRun: '' as Ref<Viewlet>,
|
||||
SuiteTestCases: '' as Ref<Viewlet>,
|
||||
ListTestCase: '' as Ref<Viewlet>
|
||||
},
|
||||
testCaseTypeStatus: {
|
||||
Draft: '' as Ref<Status>,
|
||||
ReviewRequired: '' as Ref<Status>,
|
||||
NeedFixes: '' as Ref<Status>,
|
||||
Ready: '' as Ref<Status>
|
||||
},
|
||||
taskType: {
|
||||
TestCase: '' as Ref<TestCase>
|
||||
},
|
||||
function: {
|
||||
GetTestSuiteLink: '' as Resource<(doc: Ref<Doc>) => Location>
|
||||
},
|
||||
resolver: {
|
||||
Location: '' as Resource<(loc: Location) => Promise<ResolvedLocation | undefined>>
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default testManagementPlugin
|
122
plugins/test-management/src/types.ts
Normal file
122
plugins/test-management/src/types.ts
Normal file
@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import {
|
||||
Doc,
|
||||
type CollectionSize,
|
||||
type Ref,
|
||||
type Markup,
|
||||
TypedSpace,
|
||||
CollaborativeDoc,
|
||||
AttachedDoc,
|
||||
Timestamp
|
||||
} from '@hcengineering/core'
|
||||
import { IconProps } from '@hcengineering/view'
|
||||
|
||||
/** @public */
|
||||
export enum TestCaseType {
|
||||
Functional,
|
||||
Performance,
|
||||
Regression,
|
||||
Security,
|
||||
Smoke,
|
||||
Usability
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const testCaseTypes = [
|
||||
TestCaseType.Functional,
|
||||
TestCaseType.Performance,
|
||||
TestCaseType.Regression,
|
||||
TestCaseType.Security,
|
||||
TestCaseType.Smoke,
|
||||
TestCaseType.Usability
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export enum TestCasePriority {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Urgent
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const testCasePriorities = [
|
||||
TestCasePriority.Low,
|
||||
TestCasePriority.Medium,
|
||||
TestCasePriority.High,
|
||||
TestCasePriority.Urgent
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export enum TestCaseStatus {
|
||||
Draft,
|
||||
ReadyForReview,
|
||||
FixReviewComments,
|
||||
Approved,
|
||||
Rejected
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TestProject extends TypedSpace, IconProps {
|
||||
fullDescription?: Markup
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TestSuite extends Doc {
|
||||
space: Ref<TestProject>
|
||||
name: string
|
||||
description?: string
|
||||
parent: Ref<TestSuite>
|
||||
testCases?: CollectionSize<TestCase>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TestCase extends AttachedDoc<TestSuite, 'testCases', TestProject> {
|
||||
name: string
|
||||
description: CollaborativeDoc
|
||||
type: TestCaseType
|
||||
priority: TestCasePriority
|
||||
status: TestCaseStatus
|
||||
assignee: Ref<Employee>
|
||||
attachments?: CollectionSize<Attachment>
|
||||
comments?: number
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TestRun extends Doc {
|
||||
name: string
|
||||
description: CollaborativeDoc
|
||||
dueDate?: Timestamp
|
||||
items?: CollectionSize<TestRunItem>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export enum TestRunResult {
|
||||
Passed,
|
||||
Blocked,
|
||||
Failed
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TestRunItem extends AttachedDoc {
|
||||
testRun: Ref<TestRun>
|
||||
testCase: Ref<TestCase>
|
||||
result?: TestRunResult
|
||||
comments?: number
|
||||
}
|
10
plugins/test-management/tsconfig.json
Normal file
10
plugins/test-management/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@
|
||||
{#if specials}
|
||||
<TreeNode
|
||||
_id={space?._id}
|
||||
icon={space?.icon === view.ids.IconWithEmoji ? IconWithEmoji : space?.icon ?? model.icon}
|
||||
icon={space?.icon === view.ids.IconWithEmoji ? IconWithEmoji : space?.icon ?? model?.icon}
|
||||
iconProps={space?.icon === view.ids.IconWithEmoji
|
||||
? { icon: space.color }
|
||||
: {
|
||||
|
93
plugins/view-resources/src/components/FolderTreeLevel.svelte
Normal file
93
plugins/view-resources/src/components/FolderTreeLevel.svelte
Normal file
@ -0,0 +1,93 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Doc, Ref } from '@hcengineering/core'
|
||||
import { Action, IconEdit } from '@hcengineering/ui'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
import { TreeItem, getActions as getContributedActions } from '../index'
|
||||
|
||||
export let folders: Ref<Doc>[]
|
||||
export let folderById: Map<Ref<Doc>, Doc>
|
||||
export let descendants: Map<Ref<Doc>, Doc[]>
|
||||
|
||||
export let selected: Ref<Doc> | undefined
|
||||
export let level: number = 0
|
||||
export let once: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function getTitle (doc: Doc): string {
|
||||
return (doc as any)?.title || ''
|
||||
}
|
||||
|
||||
function getDescendants (obj: Ref<Doc>): Ref<Doc>[] {
|
||||
return (descendants.get(obj) ?? []).sort((a, b) => getTitle(a).localeCompare(getTitle(b))).map((p) => p._id)
|
||||
}
|
||||
|
||||
function handleSelected (obj: Ref<Doc>): void {
|
||||
dispatch('selected', obj)
|
||||
}
|
||||
|
||||
async function getActions (obj: Doc): Promise<Action[]> {
|
||||
const result: Action[] = []
|
||||
const extraActions = await getContributedActions(client, obj)
|
||||
for (const act of extraActions) {
|
||||
result.push({
|
||||
icon: act.icon ?? IconEdit,
|
||||
label: act.label,
|
||||
action: async (ctx: any, evt: Event) => {
|
||||
const impl = await getResource(act.action)
|
||||
await impl(obj, evt, act.actionProps)
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
$: _folders = folders.map((it) => folderById.get(it)).filter((it) => it !== undefined) as Doc[]
|
||||
$: _descendants = new Map(_folders.map((it) => [it._id, getDescendants(it._id)]))
|
||||
</script>
|
||||
|
||||
{#each _folders as doc}
|
||||
{@const desc = _descendants.get(doc._id) ?? []}
|
||||
|
||||
{#if doc}
|
||||
<TreeItem
|
||||
_id={doc._id}
|
||||
folderIcon
|
||||
title={getTitle(doc)}
|
||||
selected={selected === doc._id}
|
||||
isFold
|
||||
empty={desc.length === 0}
|
||||
actions={async () => await getActions(doc)}
|
||||
{level}
|
||||
shouldTooltip
|
||||
on:click={() => {
|
||||
handleSelected(doc._id)
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="dropbox">
|
||||
{#if desc.length > 0 && !once}
|
||||
<svelte:self folders={desc} {descendants} {folderById} {selected} level={level + 1} on:selected />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</TreeItem>
|
||||
{/if}
|
||||
{/each}
|
149
plugins/view-resources/src/components/FoldersBrowser.svelte
Normal file
149
plugins/view-resources/src/components/FoldersBrowser.svelte
Normal file
@ -0,0 +1,149 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, Ref, SortingOrder, Space } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Action, IconEdit, navigate, type Location } from '@hcengineering/ui'
|
||||
import { getResource, type Resource } from '@hcengineering/platform'
|
||||
import { IntlString, Asset } from '@hcengineering/platform'
|
||||
|
||||
import { FoldersManager, FoldersStore, FoldersState } from '../stores/folderStore'
|
||||
import FolderTreeLevel from './FolderTreeLevel.svelte'
|
||||
import { TreeNode, TreeItem, getActions as getContributedActions } from '../index'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
export let titleKey: string = 'title'
|
||||
export let parentKey: string = 'parent'
|
||||
export let noParentId: Ref<Doc>
|
||||
export let getFolderLink: Resource<(doc: Ref<Doc> | undefined) => Location>
|
||||
export let allObjectsIcon: Asset
|
||||
export let allObjectsLabel: IntlString
|
||||
|
||||
export let forciblyСollapsed: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let foldersState: FoldersState = FoldersState.empty()
|
||||
|
||||
const foldersManager: FoldersManager = new FoldersManager(titleKey, parentKey, noParentId)
|
||||
|
||||
FoldersStore.subscribe((newState) => {
|
||||
foldersState = newState
|
||||
})
|
||||
|
||||
let selected: Ref<Doc> | undefined
|
||||
let visibleItem: Doc | undefined
|
||||
|
||||
const q = createQuery()
|
||||
q.query(
|
||||
_class,
|
||||
query ?? {},
|
||||
(result) => {
|
||||
foldersManager.setFolders(result)
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
name: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
async function handleFolderSelected (_id: Ref<Doc>): Promise<void> {
|
||||
selected = _id
|
||||
visibleItem = selected !== undefined ? foldersState.folderById.get(selected) : undefined
|
||||
const folder = foldersState.folderById.get(_id)
|
||||
if (getFolderLink) {
|
||||
const getFolderLinkFunction = await getResource(getFolderLink)
|
||||
navigate(getFolderLinkFunction(_id))
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAllItemsSelected (): Promise<void> {
|
||||
selected = noParentId
|
||||
visibleItem = undefined
|
||||
const getFolderLinkFunction = await getResource(getFolderLink)
|
||||
navigate(getFolderLinkFunction(undefined))
|
||||
}
|
||||
|
||||
async function getFolderActions (obj: Doc): Promise<Action[]> {
|
||||
const result: Action[] = []
|
||||
const extraActions = await getContributedActions(client, obj)
|
||||
for (const act of extraActions) {
|
||||
result.push({
|
||||
icon: act.icon ?? IconEdit,
|
||||
label: act.label,
|
||||
action: async (ctx: any, evt: Event) => {
|
||||
const impl = await getResource(act.action)
|
||||
await impl(obj, evt, act.actionProps)
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function getRootActions (): Promise<Action[]> {
|
||||
return []
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="folders-browser">
|
||||
<TreeNode
|
||||
_id={noParentId}
|
||||
icon={allObjectsIcon}
|
||||
label={allObjectsLabel}
|
||||
selected={selected === noParentId}
|
||||
type={'nested-selectable'}
|
||||
empty={foldersState?.folders?.length === 0}
|
||||
actions={() => getRootActions()}
|
||||
{forciblyСollapsed}
|
||||
on:click={handleAllItemsSelected}
|
||||
>
|
||||
<FolderTreeLevel
|
||||
folders={foldersState.folders}
|
||||
descendants={foldersState.descendants}
|
||||
folderById={foldersState.folderById}
|
||||
{selected}
|
||||
on:selected={(ev) => {
|
||||
handleFolderSelected(ev.detail)
|
||||
}}
|
||||
/>
|
||||
<svelte:fragment slot="visible">
|
||||
{#if (selected || forciblyСollapsed) && visibleItem}
|
||||
{@const folder = visibleItem}
|
||||
<TreeItem
|
||||
_id={folder._id}
|
||||
folderIcon
|
||||
iconProps={{ fill: 'var(--global-accent-IconColor)' }}
|
||||
title={foldersManager.getTitle(folder)}
|
||||
selected
|
||||
isFold
|
||||
empty
|
||||
actions={async () => await getFolderActions(folder)}
|
||||
shouldTooltip
|
||||
forciblyСollapsed
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</TreeNode>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.folders-browser {
|
||||
display: flex;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
</style>
|
@ -100,6 +100,7 @@ import ImageViewer from './components/viewer/ImageViewer.svelte'
|
||||
import VideoViewer from './components/viewer/VideoViewer.svelte'
|
||||
import PDFViewer from './components/viewer/PDFViewer.svelte'
|
||||
import TextViewer from './components/viewer/TextViewer.svelte'
|
||||
import FoldersBrowser from './components/FoldersBrowser.svelte'
|
||||
|
||||
import { blobImageMetadata, blobVideoMetadata } from './blob'
|
||||
|
||||
@ -295,7 +296,8 @@ export default async (): Promise<Resources> => ({
|
||||
ImageViewer,
|
||||
VideoViewer,
|
||||
PDFViewer,
|
||||
TextViewer
|
||||
TextViewer,
|
||||
FoldersBrowser
|
||||
},
|
||||
popup: {
|
||||
PositionElementAlignment
|
||||
|
86
plugins/view-resources/src/stores/folderStore.ts
Normal file
86
plugins/view-resources/src/stores/folderStore.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
|
||||
export class FoldersState {
|
||||
folders: Array<Ref<Doc>>
|
||||
folderById: Map<Ref<Doc>, Doc>
|
||||
descendants: Map<Ref<Doc>, Doc[]>
|
||||
|
||||
constructor (folders: Array<Ref<Doc>>, folderById: Map<Ref<Doc>, Doc>, descendants: Map<Ref<Doc>, Doc[]>) {
|
||||
this.folders = folders
|
||||
this.folderById = folderById
|
||||
this.descendants = descendants
|
||||
}
|
||||
|
||||
static empty (): FoldersState {
|
||||
return new FoldersState([], new Map<Ref<Doc>, Doc>(), new Map<Ref<Doc>, Doc[]>())
|
||||
}
|
||||
}
|
||||
|
||||
export const FoldersStore: Writable<FoldersState> = writable(FoldersState.empty())
|
||||
|
||||
export const SelectedFolderStore: Writable<Ref<Doc> | undefined> = writable(undefined)
|
||||
|
||||
export function setSelectedFolder (_id: Ref<Doc> | undefined): void {
|
||||
SelectedFolderStore.set(_id)
|
||||
}
|
||||
|
||||
export class FoldersManager {
|
||||
titleKey: string
|
||||
parentKey: string
|
||||
noParentId: Ref<Doc>
|
||||
|
||||
constructor (titleKey: string, parentKey: string, noParentId: Ref<Doc>) {
|
||||
this.titleKey = titleKey
|
||||
this.parentKey = parentKey
|
||||
this.noParentId = noParentId
|
||||
}
|
||||
|
||||
public getTitle (doc: Doc): string {
|
||||
return (doc as any)?.[this.titleKey] ?? ''
|
||||
}
|
||||
|
||||
public getParent (doc: Doc): Ref<Doc> {
|
||||
return (doc as any)?.[this.parentKey] ?? this.noParentId
|
||||
}
|
||||
|
||||
public getDescendants (descendants: Map<Ref<Doc>, Doc[]>, obj: Ref<Doc>): Array<Ref<Doc>> {
|
||||
return (descendants.get(obj) ?? [])
|
||||
.sort((a, b) => this.getTitle(a).localeCompare(this.getTitle(b)))
|
||||
.map((p) => p._id)
|
||||
}
|
||||
|
||||
public setFolders (result: Doc[]): void {
|
||||
let folders: Array<Ref<Doc>> = []
|
||||
const folderById: Map<Ref<Doc>, Doc> = new Map<Ref<Doc>, Doc>()
|
||||
const descendants: Map<Ref<Doc>, Doc[]> = new Map<Ref<Doc>, Doc[]>()
|
||||
|
||||
for (const doc of result) {
|
||||
const mappedDoc = {
|
||||
title: this.getTitle(doc),
|
||||
parent: this.getParent(doc),
|
||||
...doc
|
||||
}
|
||||
const current = descendants.get(this.getParent(mappedDoc)) ?? []
|
||||
current.push(mappedDoc)
|
||||
descendants.set(this.getParent(mappedDoc), current)
|
||||
folderById.set(mappedDoc._id, mappedDoc)
|
||||
}
|
||||
|
||||
folders = this.getDescendants(descendants, this.noParentId)
|
||||
FoldersStore.set(new FoldersState(folders, folderById, descendants))
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte'
|
||||
import {
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
Button,
|
||||
Breadcrumb,
|
||||
Component,
|
||||
IconAdd,
|
||||
Header,
|
||||
Separator,
|
||||
showPopup,
|
||||
getLocation,
|
||||
resolvedLocationStore
|
||||
} from '@hcengineering/ui'
|
||||
import { Doc, DocumentQuery, Ref, Space, mergeQueries } from '@hcengineering/core'
|
||||
import { IntlString, Asset } from '@hcengineering/platform'
|
||||
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let navigationComponent: AnyComponent
|
||||
export let navigationComponentProps: Record<string, any> | undefined = undefined
|
||||
export let navigationComponentLabel: IntlString
|
||||
export let navigationComponentIcon: Asset | undefined = undefined
|
||||
export let createComponent: AnyComponent | undefined = undefined
|
||||
export let createComponentProps: Record<string, any> = {}
|
||||
export let mainComponentLabel: IntlString
|
||||
export let mainComponentIcon: Asset | undefined = undefined
|
||||
export let query: DocumentQuery<Doc> = {}
|
||||
export let syncWithLocationQuery: boolean = true
|
||||
export let mainComponent: AnyComponent | AnySvelteComponent
|
||||
export let mainComponentProps = {}
|
||||
|
||||
let locationQuery: DocumentQuery<Doc> = {}
|
||||
let resultQuery: DocumentQuery<Doc> = {}
|
||||
let spaceQuery: DocumentQuery<Doc> = {}
|
||||
$: spaceQuery = space !== undefined ? { space } : {}
|
||||
$: resultQuery = mergeQueries(query, mergeQueries(spaceQuery, locationQuery)) ?? {}
|
||||
|
||||
if (syncWithLocationQuery) {
|
||||
locationQuery = getLocation()?.query as any
|
||||
onDestroy(
|
||||
resolvedLocationStore.subscribe((newLocation) => {
|
||||
locationQuery = newLocation?.query ?? {}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function showCreateDialog (): void {
|
||||
if (createComponent === undefined) return
|
||||
showPopup(createComponent, { ...createComponentProps, space }, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column">
|
||||
<Header adaptive={'disabled'}>
|
||||
<Breadcrumb icon={navigationComponentIcon} label={navigationComponentLabel} size={'large'} />
|
||||
<svelte:fragment slot="actions">
|
||||
{#if createComponent}
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
kind={'icon'}
|
||||
on:click={() => {
|
||||
showCreateDialog()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Header>
|
||||
<Component
|
||||
is={navigationComponent}
|
||||
props={{
|
||||
...navigationComponentProps,
|
||||
query: spaceQuery
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Separator name={'navigationSection'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column">
|
||||
<Header adaptive={'disabled'}>
|
||||
<Breadcrumb icon={mainComponentIcon} label={mainComponentLabel} size={'large'} />
|
||||
</Header>
|
||||
<Component
|
||||
is={mainComponent}
|
||||
props={{
|
||||
...(mainComponentProps ?? {}),
|
||||
query: resultQuery,
|
||||
totalQuery: resultQuery
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte'
|
||||
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { IntlString, Asset, getResource } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
@ -38,6 +39,9 @@
|
||||
ViewletPreference
|
||||
} from '@hcengineering/view'
|
||||
import { FilterBar, FilterButton, ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { ParentsNavigationModel } from '@hcengineering/workbench'
|
||||
|
||||
import ComponentNavigator from './ComponentNavigator.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
@ -51,6 +55,7 @@
|
||||
export let descriptors: Array<Ref<ViewletDescriptor>> | undefined = undefined
|
||||
export let baseQuery: DocumentQuery<Doc> | undefined = undefined
|
||||
export let modes: IModeSelector<any> | undefined = undefined
|
||||
export let navigationModel: ParentsNavigationModel | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -104,7 +109,7 @@
|
||||
|
||||
function showCreateDialog (): void {
|
||||
if (createComponent === undefined) return
|
||||
showPopup(createComponent, createComponentProps, 'top')
|
||||
showPopup(createComponent, { ...createComponentProps, space }, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -167,21 +172,44 @@
|
||||
resultQuery = { ...query, ...e.detail }
|
||||
}}
|
||||
/>
|
||||
<Component
|
||||
is={viewlet.$lookup.descriptor.component}
|
||||
props={{
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
viewlet,
|
||||
viewOptions,
|
||||
viewOptionsConfig: viewlet.viewOptions?.other,
|
||||
createItemDialog: createComponent,
|
||||
createItemLabel: createLabel,
|
||||
query: resultQuery,
|
||||
totalQuery: query,
|
||||
...viewlet.props
|
||||
}}
|
||||
/>
|
||||
{#if navigationModel?.navigationComponent === undefined}
|
||||
<Component
|
||||
is={viewlet.$lookup.descriptor.component}
|
||||
props={{
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
viewlet,
|
||||
viewOptions,
|
||||
viewOptionsConfig: viewlet.viewOptions?.other,
|
||||
createItemDialog: createComponent,
|
||||
createItemLabel: createLabel,
|
||||
query: resultQuery,
|
||||
totalQuery: query,
|
||||
...viewlet.props
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ComponentNavigator
|
||||
query={resultQuery}
|
||||
{space}
|
||||
mainComponent={viewlet.$lookup.descriptor.component}
|
||||
mainComponentProps={{
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
viewlet,
|
||||
viewOptions,
|
||||
viewOptionsConfig: viewlet.viewOptions?.other,
|
||||
createItemDialog: createComponent,
|
||||
createItemLabel: createLabel,
|
||||
query: resultQuery,
|
||||
totalQuery: query,
|
||||
...viewlet.props
|
||||
}}
|
||||
{...navigationModel}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -964,7 +964,13 @@
|
||||
{:else if specialComponent}
|
||||
<Component
|
||||
is={specialComponent.component}
|
||||
props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace }}
|
||||
props={{
|
||||
model: navigatorModel,
|
||||
...specialComponent.componentProps,
|
||||
currentSpace,
|
||||
space: currentSpace,
|
||||
navigationModel: specialComponent?.navigationModel
|
||||
}}
|
||||
on:action={(e) => {
|
||||
if (e?.detail) {
|
||||
const loc = getCurrentLocation()
|
||||
|
@ -183,6 +183,22 @@ export interface SpecialNavModel {
|
||||
notificationsCountProvider?: Resource<
|
||||
(inboxNotificationsByContext: Map<Ref<DocNotifyContext>, InboxNotification[]>) => number
|
||||
>
|
||||
navigationModel?: ParentsNavigationModel
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ParentsNavigationModel {
|
||||
navigationComponent: AnyComponent
|
||||
navigationComponentLabel: IntlString
|
||||
navigationComponentIcon?: Asset
|
||||
mainComponentLabel: IntlString
|
||||
mainComponentIcon?: Asset
|
||||
navigationComponentProps?: Record<string, any>
|
||||
syncWithLocationQuery?: boolean
|
||||
createComponent?: AnyComponent
|
||||
createComponentProps?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
|
24
rush.json
24
rush.json
@ -1151,7 +1151,7 @@
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/tags",
|
||||
"packageName": "@hcengineering/tags",
|
||||
"projectFolder": "plugins/tags",
|
||||
"shouldPublish": true
|
||||
},
|
||||
@ -1960,7 +1960,7 @@
|
||||
"packageName": "@hcengineering/diffview-resources",
|
||||
"projectFolder": "plugins/diffview-resources",
|
||||
"shouldPublish": false
|
||||
},
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/github",
|
||||
"projectFolder": "services/github/github",
|
||||
@ -2160,6 +2160,26 @@
|
||||
"packageName": "@hcengineering/scripts",
|
||||
"projectFolder": "common/scripts",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/model-test-management",
|
||||
"projectFolder": "models/test-management",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/test-management",
|
||||
"projectFolder": "plugins/test-management",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/test-management-resources",
|
||||
"projectFolder": "plugins/test-management-resources",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/test-management-assets",
|
||||
"projectFolder": "plugins/test-management-assets",
|
||||
"shouldPublish": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user