From a85d1de839683f2a0240463d7459b9936a29144b Mon Sep 17 00:00:00 2001 From: Lukas Giger Date: Thu, 12 Dec 2024 12:11:34 +0100 Subject: [PATCH] warn from using one-to-many association --- .../field/entity/FieldEntityAssociation.test.ts | 14 +++++++++++++- .../detail/field/entity/FieldEntityAssociation.tsx | 12 +++++++++++- .../mock/detail-field-entity-association.spec.ts | 8 ++++++++ .../tests/pageobjects/abstract/FieldMessage.ts | 5 +++++ .../pageobjects/field/entity/FieldAssociation.ts | 3 +++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts index 2ef5f2fe..3bf7699a 100644 --- a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts +++ b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts @@ -1,6 +1,6 @@ import type { Association, EntityClassField } from '@axonivy/dataclass-editor-protocol'; import { customRenderHook } from '../../../context/test-utils/test-utils'; -import { useCardinality, useMappedByFieldName } from './FieldEntityAssociation'; +import { cardinalityMessage, useCardinality, useMappedByFieldName } from './FieldEntityAssociation'; describe('useMappedByFieldName', () => { test('clear modifiers', () => { @@ -137,3 +137,15 @@ describe('useCardinality', () => { expect(newField.entity.orphanRemoval).toBeFalsy(); }); }); + +describe('cardinalityMessage', () => { + test('one-to-many association returns a warning', () => { + expect(cardinalityMessage('ONE_TO_MANY')).toBeDefined(); + }); + + test('other associations return nothing', () => { + expect(cardinalityMessage()).toBeUndefined(); + expect(cardinalityMessage('ONE_TO_ONE')).toBeUndefined(); + expect(cardinalityMessage('MANY_TO_ONE')).toBeUndefined(); + }); +}); diff --git a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx index 66440520..2b77b6ae 100644 --- a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx +++ b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx @@ -36,6 +36,16 @@ const cardinalityItems: Array<{ value: Association; label: string }> = [ { value: 'MANY_TO_ONE', label: 'Many-to-One' } ] as const; +export const cardinalityMessage = (cardinality?: Association) => { + if (cardinality === 'ONE_TO_MANY') { + return { + message: 'A One-to-Many association comes with a significant performance impact. Only use it if it is absolutely necessary.', + variant: 'warning' + }; + } + return; +}; + export const FieldEntityAssociation = () => { const { context } = useAppContext(); const { field, setProperty } = useFieldEntityProperty(); @@ -58,7 +68,7 @@ export const FieldEntityAssociation = () => { Association - + diff --git a/playwright/tests/integration/mock/detail-field-entity-association.spec.ts b/playwright/tests/integration/mock/detail-field-entity-association.spec.ts index 97c1b1c6..16bb0c15 100644 --- a/playwright/tests/integration/mock/detail-field-entity-association.spec.ts +++ b/playwright/tests/integration/mock/detail-field-entity-association.spec.ts @@ -9,12 +9,20 @@ test.beforeEach(async ({ page }) => { test('cardinality', async () => { const cardinality = editor.detail.field.entity.association.cardinality; + const cardinalityMessage = editor.detail.field.entity.association.cardinalityMessage; await editor.detail.dataClass.general.classType.fillValues('Entity'); await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await cardinality.expectToHaveOptions('One-to-One', 'Many-to-One'); + + await editor.table.row(6).locator.click(); + await editor.detail.field.entity.accordion.open(); + + await expect(cardinalityMessage.locator).toBeHidden(); + await cardinality.choose('One-to-Many'); + await cardinalityMessage.expectToHaveWarningMessage('A One-to-Many association comes with a significant performance impact. Only use it if it is absolutely necessary.'); }); test('cascade', async () => { diff --git a/playwright/tests/pageobjects/abstract/FieldMessage.ts b/playwright/tests/pageobjects/abstract/FieldMessage.ts index c219511a..cf921c9a 100644 --- a/playwright/tests/pageobjects/abstract/FieldMessage.ts +++ b/playwright/tests/pageobjects/abstract/FieldMessage.ts @@ -11,4 +11,9 @@ export class FieldMessage { await expect(this.locator).toHaveText(message); await expect(this.locator).toHaveAttribute('data-state', 'error'); } + + async expectToHaveWarningMessage(message: string) { + await expect(this.locator).toHaveText(message); + await expect(this.locator).toHaveAttribute('data-state', 'warning'); + } } diff --git a/playwright/tests/pageobjects/field/entity/FieldAssociation.ts b/playwright/tests/pageobjects/field/entity/FieldAssociation.ts index 194c64ae..4bad6889 100644 --- a/playwright/tests/pageobjects/field/entity/FieldAssociation.ts +++ b/playwright/tests/pageobjects/field/entity/FieldAssociation.ts @@ -1,5 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { Collapsible } from '../../abstract/Collapsible'; +import { FieldMessage } from '../../abstract/FieldMessage'; import { Select } from '../../abstract/Select'; export type FieldAssociationCascadeTypes = { [K in keyof FieldAssociation['cascadeTypes']]: boolean }; @@ -7,6 +8,7 @@ export type FieldAssociationCascadeTypes = { [K in keyof FieldAssociation['casca export class FieldAssociation { readonly collapsible: Collapsible; readonly cardinality: Select; + readonly cardinalityMessage: FieldMessage; readonly cascadeTypes: { all: Locator; persist: Locator; @@ -20,6 +22,7 @@ export class FieldAssociation { constructor(page: Page, parentLocator: Locator) { this.collapsible = new Collapsible(page, parentLocator, { label: 'Association' }); this.cardinality = new Select(page, this.collapsible.locator, { label: 'Cardinality' }); + this.cardinalityMessage = new FieldMessage(this.collapsible.locator, { label: 'Cardinality' }); this.cascadeTypes = { all: this.collapsible.locator.getByLabel('All'), persist: this.collapsible.locator.getByLabel('Persist'),