mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-29 19:56:18 +00:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
commit
3f007f25b0
@ -115,6 +115,7 @@ function defineApplication (builder: Builder): void {
|
||||
navigationComponentIcon: testManagement.icon.TestRuns,
|
||||
mainComponentLabel: testManagement.string.TestResults,
|
||||
mainComponentIcon: testManagement.icon.TestResult,
|
||||
mainHeaderComponent: testManagement.component.TestRunHeader,
|
||||
navigationComponentProps: {
|
||||
_class: testManagement.class.TestRun,
|
||||
icon: testManagement.icon.TestRuns,
|
||||
|
@ -41,6 +41,7 @@ export default mergeIds(testManagementId, testManganement, {
|
||||
RunButton: '' as AnyComponent,
|
||||
TestResultPresenter: '' as AnyComponent,
|
||||
EditTestResult: '' as AnyComponent,
|
||||
TestResultFooter: '' as AnyComponent
|
||||
TestResultFooter: '' as AnyComponent,
|
||||
TestRunHeader: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -64,4 +64,17 @@
|
||||
<symbol id="run" viewBox="-5 -3 24 24">
|
||||
<path d="M2.067,0.043C2.21-0.028,2.372-0.008,2.493,0.085l13.312,8.503c0.094,0.078,0.154,0.191,0.154,0.313c0,0.12-0.061,0.237-0.154,0.314L2.492,17.717c-0.07,0.057-0.162,0.087-0.25,0.087l-0.176-0.04c-0.136-0.065-0.222-0.207-0.222-0.361V0.402C1.844,0.25,1.93,0.107,2.067,0.043z" />
|
||||
</symbol>
|
||||
<symbol id="status-untested" viewBox="0 0 14 14" fill="#D7D8DB">
|
||||
<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-passed" viewBox="0 0 14 14" fill="var(--theme-won-color)">
|
||||
<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 M9.9,3.9c0.3-0.3,0.8-0.3,1.2,0l0,0c0.3,0.3,0.3,0.9,0,1.2l-5,5c-0.3,0.3-0.9,0.3-1.2,0l-2-2c-0.3-0.3-0.3-0.9,0-1.2c0.2-0.2,0.4-0.2,0.6-0.2s0.4,0.1,0.6,0.2l1.4,1.4l4-4v0L9.9,3.9z" />
|
||||
</symbol>
|
||||
<symbol id="status-failed" viewBox="0 0 14 14" fill="var(--theme-error-color)">
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;" d="M7,14c3.9,0,7-3.1,7-7c0-3.9-3.1-7-7-7C3.1,0,0,3.1,0,7C0,10.9,3.1,14,7,14z M5,4C4.7,3.7,4.3,3.7,4,4S3.7,4.7,4,5l2,2L4,9C3.7,9.3,3.7,9.7,4,10c0.3,0.3,0.8,0.3,1.1,0l2-2l2,2c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-2-2l2-2c0.3-0.3,0.3-0.8,0-1.1C9.7,3.7,9.3,3.7,9,4l-2,2L5,4z" />
|
||||
</symbol>
|
||||
<symbol id="status-blocked" viewBox="0 0 14 14" fill="var(--theme-warning-color)">
|
||||
<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" />
|
||||
<path d="M7,7V3.5c1.9,0,3.5,1.6,3.5,3.5S8.9,10.5,7,10.5V7z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Popis testovacího případu",
|
||||
"TestResultAttributes": "Výsledek",
|
||||
"GoToNextTest": "Další",
|
||||
"GoToNextTestTooltip": "Přejít na další test"
|
||||
"GoToNextTestTooltip": "Přejít na další test",
|
||||
"AllTests": "Všechny testy",
|
||||
"MyTests": "Moje testy",
|
||||
"Comments": "Komentáře"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Test case description",
|
||||
"TestResultAttributes": "Result",
|
||||
"GoToNextTest": "Next",
|
||||
"GoToNextTestTooltip": "Go to next test"
|
||||
"GoToNextTestTooltip": "Go to next test",
|
||||
"AllTests": "All tests",
|
||||
"MyTests": "My tests",
|
||||
"Comments": "Comments"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Descripción del caso de prueba",
|
||||
"TestResultAttributes": "Resultado",
|
||||
"GoToNextTest": "Siguiente",
|
||||
"GoToNextTestTooltip": "Ir al siguiente test"
|
||||
"GoToNextTestTooltip": "Ir al siguiente test",
|
||||
"AllTests": "Todas las pruebas",
|
||||
"MyTests": "Mis pruebas",
|
||||
"Comments": "Comentarios"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Description du cas de test",
|
||||
"TestResultAttributes": "Résultat",
|
||||
"GoToNextTest": "Suivant",
|
||||
"GoToNextTestTooltip": "Aller au test suivant"
|
||||
"GoToNextTestTooltip": "Aller au test suivant",
|
||||
"AllTests": "Tous les tests",
|
||||
"MyTests": "Mes tests",
|
||||
"Comments": "Commentaires"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Descrizione del caso di test",
|
||||
"TestResultAttributes": "Risultato",
|
||||
"GoToNextTest": "Successivo",
|
||||
"GoToNextTestTooltip": "Vai al test successivo"
|
||||
"GoToNextTestTooltip": "Vai al test successivo",
|
||||
"AllTests": "Tutte le test",
|
||||
"MyTests": "Le mie test",
|
||||
"Comments": "Commenti"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Descrição do caso de teste",
|
||||
"TestResultAttributes": "Resultado",
|
||||
"GoToNextTest": "Próximo",
|
||||
"GoToNextTestTooltip": "Ir para o próximo teste"
|
||||
"GoToNextTestTooltip": "Ir para o próximo teste",
|
||||
"AllTests": "Todos os testes",
|
||||
"MyTests": "Meus testes",
|
||||
"Comments": "Comentários"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "Описание тест-кейса",
|
||||
"TestResultAttributes": "Результат",
|
||||
"GoToNextTest": "Следующий",
|
||||
"GoToNextTestTooltip": "Перейти к следующему тесту"
|
||||
"GoToNextTestTooltip": "Перейти к следующему тесту",
|
||||
"AllTests": "Все тесты",
|
||||
"MyTests": "Мои тесты",
|
||||
"Comments": "Комментарии"
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@
|
||||
"TestCaseDescription": "測試用例描述",
|
||||
"TestResultAttributes": "結果",
|
||||
"GoToNextTest": "下一個",
|
||||
"GoToNextTestTooltip": "轉到下一個測試"
|
||||
"GoToNextTestTooltip": "轉到下一個測試",
|
||||
"AllTests": "所有測試",
|
||||
"MyTests": "我的測試",
|
||||
"Comments": "評論"
|
||||
}
|
||||
}
|
||||
|
@ -38,9 +38,9 @@ loadMetadata(testManagement.icon, {
|
||||
StatusRejected: `${icons}#status-canceled`,
|
||||
TestLibrary: `${icons}#test-library`,
|
||||
TestResult: `${icons}#testResult`,
|
||||
StatusNonTested: `${icons}#status-draft`,
|
||||
StatusBlocked: `${icons}#status-review-comments`,
|
||||
StatusPassed: `${icons}#status-approved`,
|
||||
StatusFailed: `${icons}#status-canceled`,
|
||||
StatusNonTested: `${icons}#status-untested`,
|
||||
StatusBlocked: `${icons}#status-blocked`,
|
||||
StatusPassed: `${icons}#status-passed`,
|
||||
StatusFailed: `${icons}#status-failed`,
|
||||
Run: `${icons}#run`
|
||||
})
|
||||
|
@ -15,16 +15,12 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { type Class, type Ref, Doc, Mixin, WithLookup } from '@hcengineering/core'
|
||||
import { ActionContext, createQuery } from '@hcengineering/presentation'
|
||||
import { type Class, type Ref, WithLookup } from '@hcengineering/core'
|
||||
import { TestCase, TestResult } from '@hcengineering/test-management'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { Label, Scroller } from '@hcengineering/ui'
|
||||
import { DocAttributeBar, getDocMixins } from '@hcengineering/view-resources'
|
||||
|
||||
import RightHeader from './RightHeader.svelte'
|
||||
import NextButton from './NextButton.svelte'
|
||||
import TestResultAside from './TestResultAside.svelte'
|
||||
import TestCaseDetails from '../test-case/TestCaseDetails.svelte'
|
||||
import testManagement from '../../plugin'
|
||||
|
||||
@ -36,13 +32,6 @@
|
||||
const testCase = object?.$lookup?.testCase as TestCase | undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
$: mixins = object ? getDocMixins(object, false) : []
|
||||
|
||||
let descriptionBox: AttachmentStyleBoxCollabEditor
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
@ -61,10 +50,6 @@
|
||||
}
|
||||
)
|
||||
|
||||
let content: HTMLElement
|
||||
|
||||
$: descriptionKey = hierarchy.getAttribute(testManagement.class.TestResult, 'description')
|
||||
|
||||
onMount(() => dispatch('open', { ignoreKeys: [] }))
|
||||
</script>
|
||||
|
||||
@ -77,34 +62,15 @@
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
adaptive={'default'}
|
||||
withoutActivity={true}
|
||||
on:open
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<div class="space-divider" />
|
||||
<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>
|
||||
|
||||
<svelte:fragment slot="extra">
|
||||
<NextButton {object} />
|
||||
</svelte:fragment>
|
||||
<TestCaseDetails _id={object.testCase} object={testCase} _class={testManagement.class.TestCase} />
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<DocAttributeBar {object} {mixins} ignoreKeys={['name']} />
|
||||
<RightHeader>
|
||||
<Label label={testManagement.string.TestCaseDescription} />
|
||||
</RightHeader>
|
||||
<Scroller padding={'0.5rem 2rem'}>
|
||||
<TestCaseDetails _id={object.testCase} object={testCase} _class={testManagement.class.TestCase} />
|
||||
</Scroller>
|
||||
<TestResultAside {object} />
|
||||
</svelte:fragment>
|
||||
</Panel>
|
||||
{/if}
|
||||
|
@ -1,81 +0,0 @@
|
||||
<!--
|
||||
// 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 { onMount, onDestroy } from 'svelte'
|
||||
import { Button, Loading, Location, navigate } from '@hcengineering/ui'
|
||||
import { initializeIterator, testResultIteratorProvider, testIteratorStore } from './store/testIteratorStore'
|
||||
import testManagement, { TestResult } from '@hcengineering/test-management'
|
||||
import { Doc, type DocumentQuery, WithLookup } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { getObjectLinkFragment } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
export let object: WithLookup<TestResult> | undefined
|
||||
let isLoading = true
|
||||
let hasNext = false
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const unsubscribe = testIteratorStore.subscribe(() => {
|
||||
hasNext = testResultIteratorProvider.getIterator()?.hasNext() ?? false
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
const query: DocumentQuery<TestResult> = { attachedTo: object?.attachedTo } as any
|
||||
await initializeIterator(query, object?._id)
|
||||
hasNext = testResultIteratorProvider.getIterator()?.hasNext() ?? false
|
||||
isLoading = false
|
||||
})
|
||||
onDestroy(() => {
|
||||
testResultIteratorProvider.reset()
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
async function goToNextItem (): Promise<void> {
|
||||
const iterator = testResultIteratorProvider.getIterator()
|
||||
if (iterator !== undefined) {
|
||||
const nextItem = iterator.next()
|
||||
if (nextItem === undefined) {
|
||||
console.error('No next item')
|
||||
return
|
||||
}
|
||||
const link = await getLink(nextItem)
|
||||
if (link !== undefined) {
|
||||
navigate(link)
|
||||
}
|
||||
|
||||
console.log('Next item:', nextItem)
|
||||
}
|
||||
}
|
||||
|
||||
async function getLink (object: Doc): Promise<Location> {
|
||||
const { component } = hierarchy.classHierarchyMixin(testManagement.class.TestResult, view.mixin.ObjectPanel) as any
|
||||
return await getObjectLinkFragment(hierarchy, object, {}, component)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isLoading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<Button
|
||||
label={testManagement.string.GoToNextTest}
|
||||
kind={'primary'}
|
||||
icon={view.icon.ArrowRight}
|
||||
disabled={!hasNext}
|
||||
on:click={goToNextItem}
|
||||
showTooltip={{ label: testManagement.string.GoToNextTestTooltip }}
|
||||
/>
|
||||
{/if}
|
@ -0,0 +1,76 @@
|
||||
<!--
|
||||
// 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, onMount } from 'svelte'
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Doc, Mixin, WithLookup } from '@hcengineering/core'
|
||||
import testManagement, { TestResult } from '@hcengineering/test-management'
|
||||
import { DocAttributeBar, getDocMixins } from '@hcengineering/view-resources'
|
||||
|
||||
import { Component, Label } from '@hcengineering/ui'
|
||||
import RightHeader from './RightHeader.svelte'
|
||||
|
||||
export let object: WithLookup<TestResult> | undefined
|
||||
export let withoutActivity: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let mixins: Mixin<Doc>[] = []
|
||||
$: mixins = object !== undefined ? getDocMixins(object, false) : []
|
||||
|
||||
let descriptionBox: AttachmentStyleBoxCollabEditor
|
||||
|
||||
let content: HTMLElement
|
||||
|
||||
$: descriptionKey = hierarchy.getAttribute(testManagement.class.TestResult, 'description')
|
||||
|
||||
onMount(() => dispatch('open', { ignoreKeys: [] }))
|
||||
</script>
|
||||
|
||||
{#if object}
|
||||
<DocAttributeBar {object} {mixins} ignoreKeys={['name']} />
|
||||
<RightHeader>
|
||||
<Label label={testManagement.string.Comments} />
|
||||
</RightHeader>
|
||||
<div class="w-full mt-6 px-4">
|
||||
<AttachmentStyleBoxCollabEditor
|
||||
focusIndex={30}
|
||||
{object}
|
||||
key={{ key: 'description', attr: descriptionKey }}
|
||||
bind:this={descriptionBox}
|
||||
identifier={object?._id}
|
||||
placeholder={testManagement.string.DescriptionPlaceholder}
|
||||
boundary={content}
|
||||
/>
|
||||
</div>
|
||||
{#if !withoutActivity}
|
||||
<div class="w-full mt-6 p-4">
|
||||
<Component
|
||||
is={activity.component.Activity}
|
||||
props={{
|
||||
object,
|
||||
showCommenInput: true,
|
||||
focusIndex: 1000,
|
||||
boundary: content
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
@ -0,0 +1,82 @@
|
||||
<!--
|
||||
// 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, onMount, onDestroy } from 'svelte'
|
||||
import { ActionContext } from '@hcengineering/presentation'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import testManagement, { TestResult, TestCase } from '@hcengineering/test-management'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
|
||||
import { testResultIteratorProvider, testIteratorStore } from './store/testIteratorStore'
|
||||
import TestResultAside from './TestResultAside.svelte'
|
||||
import TestCaseDetails from '../test-case/TestCaseDetails.svelte'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let object: WithLookup<TestResult> | undefined = undefined
|
||||
let testCase: TestCase | undefined = undefined
|
||||
let hasNext = false
|
||||
|
||||
const unsubscribe = testIteratorStore.subscribe(() => {
|
||||
hasNext = testResultIteratorProvider.getIterator()?.hasNext() ?? false
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
object = testResultIteratorProvider.getIterator()?.next()
|
||||
testCase = object?.$lookup?.testCase as TestCase | undefined
|
||||
})
|
||||
onDestroy(() => {
|
||||
testResultIteratorProvider.reset()
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
async function goToNextItem (): Promise<void> {
|
||||
object = testResultIteratorProvider.getIterator()?.next()
|
||||
testCase = object?.$lookup?.testCase as TestCase | undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionContext context={{ mode: 'editor' }} />
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
{object}
|
||||
title={object?.name}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
adaptive={'default'}
|
||||
withoutActivity
|
||||
on:open
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="extra">
|
||||
<Button
|
||||
label={testManagement.string.GoToNextTest}
|
||||
kind={'primary'}
|
||||
icon={view.icon.ArrowRight}
|
||||
disabled={!hasNext}
|
||||
on:click={goToNextItem}
|
||||
showTooltip={{ label: testManagement.string.GoToNextTestTooltip }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<TestCaseDetails _id={object.testCase} object={testCase} _class={testManagement.class.TestCase} />
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<TestResultAside {object} withoutActivity={true} />
|
||||
</svelte:fragment>
|
||||
</Panel>
|
||||
{/if}
|
@ -17,11 +17,11 @@ import { writable, get } from 'svelte/store'
|
||||
import {
|
||||
type IteratorState,
|
||||
type StoreAdapter,
|
||||
type IteratorParams,
|
||||
ObjectIteratorProvider,
|
||||
getDefaultIteratorState
|
||||
} from '@hcengineering/view-resources'
|
||||
import testManagement, { type TestResult } from '@hcengineering/test-management'
|
||||
import type { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
|
||||
export const testIteratorStore = writable<IteratorState<TestResult>>(getDefaultIteratorState<TestResult>({}))
|
||||
|
||||
@ -39,11 +39,8 @@ const adapter: StoreAdapter<TestResult> = {
|
||||
|
||||
export const testResultIteratorProvider = new ObjectIteratorProvider<TestResult>(adapter)
|
||||
|
||||
export async function initializeIterator (
|
||||
query: DocumentQuery<TestResult>,
|
||||
currentObject: Ref<TestResult> | undefined
|
||||
): Promise<void> {
|
||||
await testResultIteratorProvider.initialize(testManagement.class.TestResult, query, currentObject)
|
||||
export async function initializeIterator (options: IteratorParams<TestResult>): Promise<void> {
|
||||
await testResultIteratorProvider.initialize(testManagement.class.TestResult, options)
|
||||
}
|
||||
|
||||
export function resetTestObjectIterator (): void {
|
||||
|
@ -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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IModeSelector, ModeSelector, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
|
||||
import { getCurrentMode, onModeChanged } from '../../navigation'
|
||||
|
||||
export let modes: [string, IntlString, object][]
|
||||
|
||||
let mode: string | undefined = undefined
|
||||
let modeSelectorProps: IModeSelector | undefined = undefined
|
||||
$: mode = getCurrentMode($resolvedLocationStore)
|
||||
|
||||
$: if (mode === undefined) {
|
||||
;[[mode]] = modes
|
||||
}
|
||||
$: if (mode !== undefined) {
|
||||
modeSelectorProps = {
|
||||
config: modes,
|
||||
mode,
|
||||
onChange: onModeChanged
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modeSelectorProps !== undefined}
|
||||
<ModeSelector kind={'subtle'} props={modeSelectorProps} />
|
||||
{/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 { DocumentQuery, Ref, Space } from '@hcengineering/core'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
import type { TestProject, TestResult } from '@hcengineering/test-management'
|
||||
import { selectionStore } from '@hcengineering/view-resources'
|
||||
|
||||
import testManagement from '../../plugin'
|
||||
import { showTestRunnerPanel } from '../../utils'
|
||||
|
||||
export let query: DocumentQuery<TestResult> = {}
|
||||
export let space: Ref<Space>
|
||||
|
||||
const project: Ref<TestProject> = space as any
|
||||
|
||||
const handleRun = async (): Promise<void> => {
|
||||
const selectedDocs = $selectionStore?.docs ?? []
|
||||
await showTestRunnerPanel({
|
||||
query,
|
||||
space: project,
|
||||
selectedDocs: selectedDocs.length > 0 ? (selectedDocs as TestResult[]) : undefined
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button
|
||||
icon={testManagement.icon.Run}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
label={testManagement.string.RunTestCases}
|
||||
on:click={handleRun}
|
||||
/>
|
@ -0,0 +1,35 @@
|
||||
<!--
|
||||
// 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 { IntlString } from '@hcengineering/platform'
|
||||
import testManagement, { type TestResult } from '@hcengineering/test-management'
|
||||
import { DocumentQuery, Ref, Space } from '@hcengineering/core'
|
||||
|
||||
import TestResultModeSelector from './TestResultModeSelector.svelte'
|
||||
import TestRunButton from './TestRunButton.svelte'
|
||||
|
||||
export let query: DocumentQuery<TestResult> = {}
|
||||
export let space: Ref<Space>
|
||||
|
||||
const modes: [string, IntlString, object][] = [
|
||||
[testManagement.mode.AllTests, testManagement.string.AllTests, {}],
|
||||
[testManagement.mode.MyTests, testManagement.string.MyTests, {}]
|
||||
]
|
||||
</script>
|
||||
|
||||
{#if modes !== undefined}
|
||||
<TestResultModeSelector {modes} />
|
||||
<TestRunButton {query} {space} />
|
||||
{/if}
|
@ -36,6 +36,8 @@ import TestResultPresenter from './components/test-result/TestResultPresenter.sv
|
||||
import EditTestResult from './components/test-result/EditTestResult.svelte'
|
||||
import TestResultHeader from './components/test-result/TestResultHeader.svelte'
|
||||
import TestResultFooter from './components/test-result/TestResultFooter.svelte'
|
||||
import TestRunHeader from './components/test-run/TestRunHeader.svelte'
|
||||
import TestRunner from './components/test-result/TestRunner.svelte'
|
||||
|
||||
import { CreateChildTestSuiteAction, EditTestSuiteAction, RunSelectedTestsAction } from './utils'
|
||||
import { resolveLocation, getAttachedObjectLink } from './navigation'
|
||||
@ -63,7 +65,9 @@ export default async (): Promise<Resources> => ({
|
||||
TestResultPresenter,
|
||||
EditTestResult,
|
||||
TestResultHeader,
|
||||
TestResultFooter
|
||||
TestResultFooter,
|
||||
TestRunHeader,
|
||||
TestRunner
|
||||
},
|
||||
function: {
|
||||
GetTestSuiteLink: getAttachedObjectLink,
|
||||
|
@ -11,15 +11,21 @@
|
||||
//
|
||||
// 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 testManagement, {
|
||||
testManagementId,
|
||||
type TestSuite,
|
||||
type TestProject,
|
||||
type TestRun
|
||||
} from '@hcengineering/test-management'
|
||||
import { type Doc, type Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
getCurrentResolvedLocation,
|
||||
getLocation,
|
||||
getPanelURI,
|
||||
type Location,
|
||||
type ResolvedLocation
|
||||
type ResolvedLocation,
|
||||
navigate
|
||||
} from '@hcengineering/ui'
|
||||
import view, { type ObjectPanel } from '@hcengineering/view'
|
||||
import { accessDeniedStore } from '@hcengineering/view-resources'
|
||||
@ -63,7 +69,7 @@ async function generateProjectLocation (
|
||||
|
||||
export function getAttachedObjectLink (parentDoc: Ref<Doc>): Location {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.query = parentDoc === undefined ? undefined : { attachedTo: parentDoc }
|
||||
loc.query = parentDoc === undefined ? undefined : { ...loc.query, attachedTo: parentDoc }
|
||||
|
||||
return loc
|
||||
}
|
||||
@ -79,6 +85,11 @@ export function getTestSuiteIdFromLocation (): Ref<TestSuite> {
|
||||
return (location?.query?.[SUITE_KEY] as Ref<TestSuite>) ?? testManagement.ids.NoParent
|
||||
}
|
||||
|
||||
export function getTestRunIdFromLocation (): Ref<TestRun> {
|
||||
const location = getLocation()
|
||||
return (location?.query?.[SUITE_KEY] as Ref<TestRun>) ?? testManagement.ids.NoTestRun
|
||||
}
|
||||
|
||||
export function getTestRunsLink (space: Ref<TestProject>, parentDoc: Ref<Doc>): Location {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.path.length = 5
|
||||
@ -91,6 +102,29 @@ export function getTestRunsLink (space: Ref<TestProject>, parentDoc: Ref<Doc>):
|
||||
return loc
|
||||
}
|
||||
|
||||
export function onModeChanged (newMode: string): void {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
const { assignee, ...baseQuery } = loc.query ?? {}
|
||||
const currentUser = getCurrentAccount()?.person
|
||||
if (currentUser === undefined) {
|
||||
console.error('Current user is not defined')
|
||||
return
|
||||
}
|
||||
switch (newMode) {
|
||||
case testManagement.mode.AllTests:
|
||||
loc.query = baseQuery
|
||||
break
|
||||
case testManagement.mode.MyTests:
|
||||
loc.query = { ...baseQuery, assignee: currentUser }
|
||||
}
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
export function getCurrentMode (loc: Location): string {
|
||||
const { assignee } = loc.query ?? {}
|
||||
return assignee === getCurrentAccount()?.person ? testManagement.mode.MyTests : testManagement.mode.AllTests
|
||||
}
|
||||
|
||||
export async function resolveLocation (loc: Location): Promise<ResolvedLocation | undefined> {
|
||||
if (loc.path[2] !== testManagementId) {
|
||||
return undefined
|
||||
|
@ -13,15 +13,19 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import type { Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { type TestProject, type TestCase, type TestSuite } from '@hcengineering/test-management'
|
||||
import { showPopup, showPanel } from '@hcengineering/ui'
|
||||
import { type TestProject, type TestCase, type TestSuite, type TestResult } from '@hcengineering/test-management'
|
||||
import testManagement from '@hcengineering/test-management'
|
||||
|
||||
import CreateTestSuiteComponent from './components/test-suite/CreateTestSuite.svelte'
|
||||
import EditTestSuiteComponent from './components/test-suite/EditTestSuite.svelte'
|
||||
import CreateTestCase from './components/test-case/CreateTestCase.svelte'
|
||||
import CreateProject from './components/project/CreateProject.svelte'
|
||||
import CreateTestRun from './components/test-run/CreateTestRun.svelte'
|
||||
import { getTestRunIdFromLocation } from './navigation'
|
||||
import { initializeIterator } from './components/test-result/store/testIteratorStore'
|
||||
|
||||
export async function showCreateTestSuitePopup (
|
||||
space: Ref<TestProject> | undefined,
|
||||
@ -50,6 +54,30 @@ export async function showCreateTestRunPopup (options: {
|
||||
showPopup(CreateTestRun, options, 'top')
|
||||
}
|
||||
|
||||
export async function showTestRunnerPanel (options: {
|
||||
query?: DocumentQuery<TestResult>
|
||||
space: Ref<TestProject>
|
||||
selectedDocs?: TestResult[]
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const { query, space, selectedDocs } = options
|
||||
await initializeIterator({
|
||||
query: { ...query, space },
|
||||
options: {
|
||||
lookup: {
|
||||
testCase: testManagement.class.TestCase
|
||||
}
|
||||
},
|
||||
docs: selectedDocs
|
||||
})
|
||||
const testRunId = getTestRunIdFromLocation()
|
||||
showPanel(testManagement.component.TestRunner, testRunId, testManagement.class.TestRun, 'content')
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
console.error('Failed to initialize test runner', err)
|
||||
}
|
||||
}
|
||||
|
||||
export async function CreateChildTestSuiteAction (doc: TestSuite): Promise<void> {
|
||||
await showCreateTestSuitePopup(doc.space, doc._id)
|
||||
}
|
||||
|
@ -187,7 +187,10 @@ export const testManagementPlugin = plugin(testManagementId, {
|
||||
TestCaseDescription: '' as IntlString,
|
||||
TestResultAttributes: '' as IntlString,
|
||||
GoToNextTest: '' as IntlString,
|
||||
GoToNextTestTooltip: '' as IntlString
|
||||
GoToNextTestTooltip: '' as IntlString,
|
||||
AllTests: '' as IntlString,
|
||||
MyTests: '' as IntlString,
|
||||
Comments: '' as IntlString
|
||||
},
|
||||
category: {
|
||||
TestManagement: '' as Ref<ActionCategory>
|
||||
@ -206,11 +209,17 @@ export const testManagementPlugin = plugin(testManagementId, {
|
||||
TestResultStatusPresenter: '' as AnyComponent,
|
||||
TestResultStatusEditor: '' as AnyComponent,
|
||||
TestRunResult: '' as AnyComponent,
|
||||
TestResultHeader: '' as AnyComponent
|
||||
TestResultHeader: '' as AnyComponent,
|
||||
TestRunner: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
NoParent: '' as Ref<TestSuite>,
|
||||
TestCaseUpdatedActivityViewlet: '' as Ref<TestCase>
|
||||
TestCaseUpdatedActivityViewlet: '' as Ref<TestCase>,
|
||||
NoTestRun: '' as Ref<TestRun>
|
||||
},
|
||||
mode: {
|
||||
AllTests: '' as IntlString,
|
||||
MyTests: '' as IntlString
|
||||
},
|
||||
spaceType: {
|
||||
TestCaseType: '' as Ref<SpaceType>,
|
||||
|
@ -49,7 +49,7 @@ describe('ObjectIterator', () => {
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new ObjectIterator(_class, query, storeAdapter)
|
||||
new ObjectIterator(_class, storeAdapter, { query })
|
||||
expect(storeAdapter.set).toHaveBeenCalledWith({
|
||||
query,
|
||||
currentObjects: [],
|
||||
@ -64,7 +64,7 @@ describe('ObjectIterator', () => {
|
||||
const query: DocumentQuery<Doc> = { key: 'value' }
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
|
||||
const iterator = new ObjectIterator(_class, query, storeAdapter)
|
||||
const iterator = new ObjectIterator(_class, storeAdapter, { query })
|
||||
await iterator.loadObjects(undefined)
|
||||
|
||||
expect(findAll).toHaveBeenCalledWith(_class, query, {
|
||||
@ -81,7 +81,7 @@ describe('ObjectIterator', () => {
|
||||
mockObjects = [{ id: '1' }, { id: '2' }, { id: '3' }] as any
|
||||
const query: DocumentQuery<Doc> = { key: 'value' }
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
const iterator = new ObjectIterator(_class, query, storeAdapter)
|
||||
const iterator = new ObjectIterator(_class, storeAdapter, { query })
|
||||
await iterator.loadObjects(undefined)
|
||||
let nextObject = iterator.next()
|
||||
|
||||
@ -102,7 +102,7 @@ describe('ObjectIterator', () => {
|
||||
mockObjects = [] as any
|
||||
const query: DocumentQuery<Doc> = { key: 'value' }
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
const iterator = new ObjectIterator(_class, query, storeAdapter)
|
||||
const iterator = new ObjectIterator(_class, storeAdapter, { query })
|
||||
await iterator.loadObjects(undefined)
|
||||
const nextObject = iterator.next()
|
||||
|
||||
@ -114,10 +114,10 @@ describe('ObjectIterator', () => {
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
const provider = new ObjectIteratorProvider(storeAdapter)
|
||||
|
||||
await provider.initialize(_class, query, undefined)
|
||||
await provider.initialize(_class, { query })
|
||||
const firstIterator = provider.getIterator()
|
||||
|
||||
await provider.initialize(_class, query, undefined)
|
||||
await provider.initialize(_class, { query })
|
||||
const secondIterator = provider.getIterator()
|
||||
|
||||
expect(firstIterator).toBe(secondIterator)
|
||||
@ -129,7 +129,7 @@ describe('ObjectIterator', () => {
|
||||
const _class: Ref<Class<Doc>> = { id: 'class1' } as any
|
||||
const provider = new ObjectIteratorProvider(storeAdapter)
|
||||
|
||||
await provider.initialize(_class, query, undefined)
|
||||
await provider.initialize(_class, { query })
|
||||
provider.reset()
|
||||
|
||||
expect(storeAdapter.set).toHaveBeenCalledWith({
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import type { DocumentQuery, Doc, Ref, Class } from '@hcengineering/core'
|
||||
import type { DocumentQuery, Doc, Ref, Class, FindOptions } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export interface IteratorState<T extends Doc> {
|
||||
@ -28,10 +28,16 @@ export interface StoreAdapter<T extends Doc> {
|
||||
get: () => IteratorState<T>
|
||||
}
|
||||
|
||||
export function getDefaultIteratorState<T extends Doc> (query: DocumentQuery<T>): IteratorState<T> {
|
||||
export interface IteratorParams<T extends Doc> {
|
||||
docs?: T[]
|
||||
query?: DocumentQuery<T>
|
||||
options?: FindOptions<T> | undefined
|
||||
}
|
||||
|
||||
export function getDefaultIteratorState<T extends Doc> (params: IteratorParams<T>): IteratorState<T> {
|
||||
return {
|
||||
query,
|
||||
currentObjects: [],
|
||||
query: params.query ?? {},
|
||||
currentObjects: params.docs ?? [],
|
||||
iteratorIndex: 0,
|
||||
limit: 100
|
||||
}
|
||||
@ -41,25 +47,23 @@ export class ObjectIterator<T extends Doc> {
|
||||
private readonly storeAdapter: StoreAdapter<T>
|
||||
private readonly class: Ref<Class<T>>
|
||||
|
||||
constructor (_class: Ref<Class<T>>, query: DocumentQuery<T>, storeAdapter: StoreAdapter<T>) {
|
||||
constructor (_class: Ref<Class<T>>, storeAdapter: StoreAdapter<T>, params: IteratorParams<T>) {
|
||||
this.class = _class
|
||||
this.storeAdapter = storeAdapter
|
||||
this.storeAdapter.set(getDefaultIteratorState<T>(query))
|
||||
this.storeAdapter.set(getDefaultIteratorState<T>(params))
|
||||
}
|
||||
|
||||
async loadObjects (currentObject: Ref<Doc> | undefined): Promise<void> {
|
||||
async loadObjects (options?: FindOptions<T> | undefined): Promise<void> {
|
||||
const client = getClient()
|
||||
const { query, limit } = this.storeAdapter.get()
|
||||
const testResults = await client.findAll(this.class, query, {
|
||||
...options,
|
||||
limit,
|
||||
total: true
|
||||
})
|
||||
this.storeAdapter.update((store) => {
|
||||
store.currentObjects = [...store.currentObjects, ...testResults]
|
||||
store.limit = testResults.total
|
||||
if (currentObject !== undefined) {
|
||||
store.iteratorIndex = store.currentObjects.findIndex((obj) => obj._id === currentObject) ?? 0
|
||||
}
|
||||
return store
|
||||
})
|
||||
}
|
||||
@ -68,8 +72,8 @@ export class ObjectIterator<T extends Doc> {
|
||||
let nextObject
|
||||
this.storeAdapter.update((store) => {
|
||||
if (store.iteratorIndex < store.currentObjects.length) {
|
||||
store.iteratorIndex += 1
|
||||
nextObject = store.currentObjects[store.iteratorIndex]
|
||||
store.iteratorIndex += 1
|
||||
}
|
||||
return store
|
||||
})
|
||||
@ -78,7 +82,7 @@ export class ObjectIterator<T extends Doc> {
|
||||
|
||||
hasNext (): boolean {
|
||||
const { currentObjects, iteratorIndex } = this.storeAdapter.get()
|
||||
return iteratorIndex < currentObjects.length - 1
|
||||
return iteratorIndex < currentObjects.length
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,10 +91,12 @@ export class ObjectIteratorProvider<T extends Doc> {
|
||||
|
||||
constructor (private readonly storeAdapter: StoreAdapter<T>) {}
|
||||
|
||||
async initialize (_class: Ref<Class<T>>, query: DocumentQuery<T>, currentObject: Ref<Doc> | undefined): Promise<void> {
|
||||
async initialize (_class: Ref<Class<T>>, params: IteratorParams<T>): Promise<void> {
|
||||
if (this.objectIterator === undefined) {
|
||||
this.objectIterator = new ObjectIterator(_class, query, this.storeAdapter)
|
||||
await this.objectIterator.loadObjects(currentObject)
|
||||
this.objectIterator = new ObjectIterator(_class, this.storeAdapter, params)
|
||||
if (params.docs === undefined || params.docs.length === 0) {
|
||||
await this.objectIterator.loadObjects(params.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ import {
|
||||
isDataField,
|
||||
isOwner,
|
||||
type JoinProps,
|
||||
NumericTypes,
|
||||
parseDoc,
|
||||
parseDocWithProjection,
|
||||
parseUpdate,
|
||||
@ -920,7 +921,12 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
continue
|
||||
}
|
||||
if (typeof val === 'number') {
|
||||
res.push(`${this.getKey(_class, baseDomain, key, joins)} ${val === 1 ? 'ASC' : 'DESC'}`)
|
||||
const attr = this.hierarchy.findAttribute(_class, key)
|
||||
if (attr !== undefined && NumericTypes.includes(attr.type._class)) {
|
||||
res.push(`(${this.getKey(_class, baseDomain, key, joins)})::numeric ${val === 1 ? 'ASC' : 'DESC'}`)
|
||||
} else {
|
||||
res.push(`${this.getKey(_class, baseDomain, key, joins)} ${val === 1 ? 'ASC' : 'DESC'}`)
|
||||
}
|
||||
} else {
|
||||
// todo handle custom sorting
|
||||
}
|
||||
|
@ -64,6 +64,13 @@ export async function retryTxn (
|
||||
})
|
||||
}
|
||||
|
||||
export const NumericTypes = [
|
||||
core.class.TypeNumber,
|
||||
core.class.TypeTimestamp,
|
||||
core.class.TypeDate,
|
||||
core.class.Collection
|
||||
]
|
||||
|
||||
export async function createTables (client: postgres.Sql, domains: string[]): Promise<void> {
|
||||
const filtered = domains.filter((d) => !loadedDomains.has(d))
|
||||
if (filtered.length === 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user