diff --git a/integrations/standalone/src/mock/dataclass-client-mock.ts b/integrations/standalone/src/mock/dataclass-client-mock.ts index bb45591c..49c89d22 100644 --- a/integrations/standalone/src/mock/dataclass-client-mock.ts +++ b/integrations/standalone/src/mock/dataclass-client-mock.ts @@ -1,5 +1,11 @@ -import type { DataActionArgs, DataClassData, ValidationResult } from '@axonivy/dataclass-editor-protocol/src/editor'; -import type { Client, Event, FunctionRequestTypes, MetaRequestTypes } from '@axonivy/dataclass-editor-protocol/src/types'; +import type { DataActionArgs, DataClassData, FieldContext, ValidationResult } from '@axonivy/dataclass-editor-protocol/src/editor'; +import type { + Client, + Event, + FunctionRequestTypes, + MappedByFieldsContext, + MetaRequestTypes +} from '@axonivy/dataclass-editor-protocol/src/types'; export class DataClassClientMock implements Client { private dataClassData: DataClassData = { @@ -42,6 +48,20 @@ export class DataClassClientMock implements Client { modifiers: [], comment: 'Transcript of the conversation.', annotations: [] + }, + { + name: 'entity', + type: 'mock.Entity', + modifiers: ['PERSISTENT'], + comment: 'An entity.', + annotations: [] + }, + { + name: 'entities', + type: 'List', + modifiers: ['PERSISTENT'], + comment: 'A list of entities.', + annotations: [] } ] }, @@ -70,16 +90,18 @@ export class DataClassClientMock implements Client { } } - meta(path: TMeta): Promise { + meta(path: TMeta, args: MetaRequestTypes[TMeta][0]): Promise { switch (path) { case 'meta/scripting/ivyTypes': return Promise.resolve([]); case 'meta/scripting/dataClasses': return Promise.resolve([]); - case 'meta/scripting/cardinalities': - return Promise.resolve(['ONE_TO_ONE', 'ONE_TO_MANY', 'MANY_TO_ONE']); - case 'meta/scripting/mappedByFields': - return Promise.resolve(['MappedByFieldName']); + case 'meta/scripting/cardinalities': { + return Promise.resolve(cardinalities(args as FieldContext)); + } + case 'meta/scripting/mappedByFields': { + return Promise.resolve(mappedByFields(args as MappedByFieldsContext)); + } default: throw Error('mock meta path not programmed'); } @@ -91,3 +113,22 @@ export class DataClassClientMock implements Client { onDataChanged: Event; } + +const cardinalities = (context: FieldContext) => { + const cardinalities = []; + if (context.field.startsWith('entity')) { + cardinalities.push('ONE_TO_ONE', 'MANY_TO_ONE'); + } + if (context.field.startsWith('entities')) { + cardinalities.push('ONE_TO_MANY'); + } + return cardinalities; +}; + +const mappedByFields = (context: MappedByFieldsContext) => { + const mappedByFields = []; + if (context.cardinality === 'ONE_TO_ONE') { + mappedByFields.push('MappedByFieldName'); + } + return mappedByFields; +}; diff --git a/packages/dataclass-editor/src/data/dataclass-utils.test.ts b/packages/dataclass-editor/src/data/dataclass-utils.test.ts index e9c1aa69..2f39df12 100644 --- a/packages/dataclass-editor/src/data/dataclass-utils.test.ts +++ b/packages/dataclass-editor/src/data/dataclass-utils.test.ts @@ -1,5 +1,5 @@ -import type { DataClass } from '@axonivy/dataclass-editor-protocol'; -import { classTypeOf, isEntity } from './dataclass-utils'; +import type { DataClass, Field, Modifier } from '@axonivy/dataclass-editor-protocol'; +import { classTypeOf, isEntity, isEntityField } from './dataclass-utils'; describe('classTypeOf', () => { test('data', () => { @@ -29,3 +29,22 @@ describe('isEntity', () => { expect(isEntity(dataClass)).toBeFalsy(); }); }); + +describe('isEntityField', () => { + test('true', () => { + const field = { modifiers: ['PERSISTENT'], entity: {} } as Field; + expect(isEntityField(field)).toBeTruthy(); + }); + + describe('false', () => { + test('missing modifier persistent', () => { + const field = { modifiers: [] as Array, entity: {} } as Field; + expect(isEntityField(field)).toBeFalsy(); + }); + + test('missing entity', () => { + const field = { modifiers: ['PERSISTENT'] } as Field; + expect(isEntityField(field)).toBeFalsy(); + }); + }); +}); diff --git a/packages/dataclass-editor/src/data/dataclass-utils.ts b/packages/dataclass-editor/src/data/dataclass-utils.ts index cdaa9c1b..8ad32731 100644 --- a/packages/dataclass-editor/src/data/dataclass-utils.ts +++ b/packages/dataclass-editor/src/data/dataclass-utils.ts @@ -1,4 +1,12 @@ -import { type DataClass, type Modifier, type DataClassType, type EntityDataClass } from '@axonivy/dataclass-editor-protocol'; +import { + type Association, + type DataClass, + type DataClassType, + type EntityClassField, + type EntityDataClass, + type Field, + type Modifier +} from '@axonivy/dataclass-editor-protocol'; export const classTypeOf = (dataClass: DataClass): DataClassType => { if (dataClass.entity) { @@ -14,6 +22,10 @@ export const isEntity = (dataClass: DataClass): dataClass is EntityDataClass => return !!dataClass.entity; }; +export const isEntityField = (field: Field): field is EntityClassField => { + return !!field.entity && field.modifiers.includes('PERSISTENT'); +}; + export const updateModifiers = (add: boolean, newModifiers: Array, modifier: Modifier) => { if (add) { if (modifier === 'ID' || modifier === 'VERSION') { @@ -28,3 +40,9 @@ export const updateModifiers = (add: boolean, newModifiers: Array, mod } return newModifiers; }; + +export const updateCardinality = (newField: EntityClassField, association?: Association) => { + newField.entity.mappedByFieldName = ''; + newField.entity.orphanRemoval = false; + newField.entity.association = association; +}; diff --git a/packages/dataclass-editor/src/detail/field/FieldDetailContent.test.ts b/packages/dataclass-editor/src/detail/field/FieldDetailContent.test.ts deleted file mode 100644 index 0accaf49..00000000 --- a/packages/dataclass-editor/src/detail/field/FieldDetailContent.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Field, Modifier } from '@axonivy/dataclass-editor-protocol'; -import { isEntityField } from './FieldDetailContent'; - -describe('isEntityField', () => { - test('true', () => { - const field = { modifiers: ['PERSISTENT'], entity: {} } as Field; - expect(isEntityField(field)).toBeTruthy(); - }); - - describe('false', () => { - test('missing modifier persistent', () => { - const field = { modifiers: [] as Array, entity: {} } as Field; - expect(isEntityField(field)).toBeFalsy(); - }); - - test('missing entity', () => { - const field = { modifiers: ['PERSISTENT'] } as Field; - expect(isEntityField(field)).toBeFalsy(); - }); - }); -}); diff --git a/packages/dataclass-editor/src/detail/field/FieldDetailContent.tsx b/packages/dataclass-editor/src/detail/field/FieldDetailContent.tsx index eb1ebc33..c4caae8c 100644 --- a/packages/dataclass-editor/src/detail/field/FieldDetailContent.tsx +++ b/packages/dataclass-editor/src/detail/field/FieldDetailContent.tsx @@ -1,6 +1,6 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Flex } from '@axonivy/ui-components'; import { EntityFieldProvider, useField } from '../../context/FieldContext'; -import type { Field, EntityClassField } from '@axonivy/dataclass-editor-protocol'; +import { isEntityField } from '../../data/dataclass-utils'; import { AnnotationsTable } from '../AnnotationsTable'; import { FieldEntityAssociation } from './entity/FieldEntityAssociation'; import { FieldEntityDatabaseField } from './entity/FieldEntityDatabaseField'; @@ -8,10 +8,6 @@ import { FieldNameTypeComment } from './FieldNameTypeComment'; import { FieldProperties } from './FieldProperties'; import { useFieldProperty } from './useFieldProperty'; -export const isEntityField = (field: Field): field is EntityClassField => { - return !!field.entity && field.modifiers.includes('PERSISTENT'); -}; - export const FieldDetailContent = () => { const { field, setField } = useField(); const { setProperty } = useFieldProperty(); diff --git a/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.test.ts b/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.test.ts index 0433f76f..22fe8d33 100644 --- a/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.test.ts +++ b/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.test.ts @@ -1,5 +1,5 @@ -import { customRenderHook } from '../../context/test-utils/test-utils'; import type { Field } from '@axonivy/dataclass-editor-protocol'; +import { customRenderHook } from '../../context/test-utils/test-utils'; import { useType } from './FieldNameTypeComment'; describe('useType', () => { @@ -34,4 +34,26 @@ describe('useType', () => { expect(newField.type).toEqual('String'); expect(newField.modifiers).toEqual(['PERSISTENT']); }); + + test('clear association', () => { + const field = { + type: 'String', + modifiers: ['PERSISTENT'], + entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'MappedByFieldName', orphanRemoval: true } + } as Field; + let newField = {} as Field; + const view = customRenderHook(() => useType(), { + wrapperProps: { fieldContext: { field, setField: field => (newField = field) } } + }); + expect(view.result.current.type).toEqual('String'); + + const originalField = structuredClone(field); + view.result.current.setType('Integer'); + expect(field).toEqual(originalField); + + expect(newField.type).toEqual('Integer'); + expect(newField.entity!.association).toBeUndefined(); + expect(newField.entity!.mappedByFieldName).toEqual(''); + expect(newField.entity!.orphanRemoval).toBeFalsy(); + }); }); diff --git a/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.tsx b/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.tsx index a04bfcf7..9d63aca9 100644 --- a/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.tsx +++ b/packages/dataclass-editor/src/detail/field/FieldNameTypeComment.tsx @@ -1,9 +1,9 @@ +import { isIDType, isVersionType } from '@axonivy/dataclass-editor-protocol'; import { BasicField, BasicInput, Collapsible, CollapsibleContent, CollapsibleTrigger, Flex, Textarea } from '@axonivy/ui-components'; import { useField } from '../../context/FieldContext'; -import { isIDType, isVersionType } from '@axonivy/dataclass-editor-protocol'; -import { updateModifiers } from '../../data/dataclass-utils'; -import { useFieldProperty } from './useFieldProperty'; +import { isEntityField, updateCardinality, updateModifiers } from '../../data/dataclass-utils'; import { InputFieldWithTypeBrowser } from './InputFieldWithTypeBrowser'; +import { useFieldProperty } from './useFieldProperty'; export const useType = () => { const { field, setField } = useField(); @@ -15,6 +15,9 @@ export const useType = () => { if (!isVersionType(type)) { newField.modifiers = updateModifiers(false, newField.modifiers, 'VERSION'); } + if (isEntityField(newField)) { + updateCardinality(newField); + } newField.type = type; setField(newField); }; 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 f1a4ced9..2ef5f2fe 100644 --- a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts +++ b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.test.ts @@ -1,5 +1,5 @@ -import { customRenderHook } from '../../../context/test-utils/test-utils'; import type { Association, EntityClassField } from '@axonivy/dataclass-editor-protocol'; +import { customRenderHook } from '../../../context/test-utils/test-utils'; import { useCardinality, useMappedByFieldName } from './FieldEntityAssociation'; describe('useMappedByFieldName', () => { @@ -61,86 +61,79 @@ describe('useMappedByFieldName', () => { }); describe('useCardinality', () => { - const expectAssociation = ( - field: EntityClassField, - association: Association | undefined, - mappedByFieldName: string, - orphanRemoval: boolean - ) => { - expect(field.entity.association).toEqual(association); - expect(field.entity.mappedByFieldName).toEqual(mappedByFieldName); - expect(field.entity.orphanRemoval).toEqual(orphanRemoval); - }; - - describe('clear properties', () => { - test('to none', () => { - const field = { - entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } - } as EntityClassField; - let newField = {} as EntityClassField; - const view = customRenderHook(() => useCardinality(), { - wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } - }); - expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); - - const originalField = structuredClone(field); - view.result.current.setCardinality(undefined as unknown as Association); - expect(field).toEqual(originalField); - - expectAssociation(newField, undefined, '', false); + test('to none', () => { + const field = { + entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } + } as EntityClassField; + let newField = {} as EntityClassField; + const view = customRenderHook(() => useCardinality(), { + wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } }); + expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); - test('to many-to-one', () => { - const field = { - entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } - } as EntityClassField; - let newField = {} as EntityClassField; - const view = customRenderHook(() => useCardinality(), { - wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } - }); - expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); + const originalField = structuredClone(field); + view.result.current.setCardinality(undefined as unknown as Association); + expect(field).toEqual(originalField); - const originalDataClass = structuredClone(field); - view.result.current.setCardinality('MANY_TO_ONE'); - expect(field).toEqual(originalDataClass); + expect(newField.entity.association).toBeUndefined(); + expect(newField.entity.mappedByFieldName).toEqual(''); + expect(newField.entity.orphanRemoval).toBeFalsy(); + }); - expectAssociation(newField, 'MANY_TO_ONE', '', false); + test('to many-to-one', () => { + const field = { + entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } + } as EntityClassField; + let newField = {} as EntityClassField; + const view = customRenderHook(() => useCardinality(), { + wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } }); - }); + expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); - describe('keep properties', () => { - test('to one-to-one', () => { - const field = { - entity: { association: 'ONE_TO_MANY', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } - } as EntityClassField; - let newField = {} as EntityClassField; - const view = customRenderHook(() => useCardinality(), { - wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } - }); - expect(view.result.current.cardinality).toEqual('ONE_TO_MANY'); + const originalDataClass = structuredClone(field); + view.result.current.setCardinality('MANY_TO_ONE'); + expect(field).toEqual(originalDataClass); - const originalField = structuredClone(field); - view.result.current.setCardinality('ONE_TO_ONE'); - expect(field).toEqual(originalField); + expect(newField.entity.association).toEqual('MANY_TO_ONE'); + expect(newField.entity.mappedByFieldName).toEqual(''); + expect(newField.entity.orphanRemoval).toBeFalsy(); + }); - expectAssociation(newField, 'ONE_TO_ONE', 'mappedByFieldName', true); + test('to one-to-one', () => { + const field = { + entity: { association: 'ONE_TO_MANY', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } + } as EntityClassField; + let newField = {} as EntityClassField; + const view = customRenderHook(() => useCardinality(), { + wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } }); + expect(view.result.current.cardinality).toEqual('ONE_TO_MANY'); - test('to many-to-one', () => { - const field = { - entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } - } as EntityClassField; - let newField = {} as EntityClassField; - const view = customRenderHook(() => useCardinality(), { - wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } - }); - expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); + const originalField = structuredClone(field); + view.result.current.setCardinality('ONE_TO_ONE'); + expect(field).toEqual(originalField); - const originalField = structuredClone(field); - view.result.current.setCardinality('ONE_TO_MANY'); - expect(field).toEqual(originalField); + expect(newField.entity.association).toEqual('ONE_TO_ONE'); + expect(newField.entity.mappedByFieldName).toEqual(''); + expect(newField.entity.orphanRemoval).toBeFalsy(); + }); - expectAssociation(newField, 'ONE_TO_MANY', 'mappedByFieldName', true); + test('to many-to-one', () => { + const field = { + entity: { association: 'ONE_TO_ONE', mappedByFieldName: 'mappedByFieldName', orphanRemoval: true } + } as EntityClassField; + let newField = {} as EntityClassField; + const view = customRenderHook(() => useCardinality(), { + wrapperProps: { entityFieldContext: { field, setField: field => (newField = field) } } }); + expect(view.result.current.cardinality).toEqual('ONE_TO_ONE'); + + const originalField = structuredClone(field); + view.result.current.setCardinality('ONE_TO_MANY'); + expect(field).toEqual(originalField); + + expect(newField.entity.association).toEqual('ONE_TO_MANY'); + expect(newField.entity.mappedByFieldName).toEqual(''); + expect(newField.entity.orphanRemoval).toBeFalsy(); }); }); diff --git a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx index 37b453db..66440520 100644 --- a/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx +++ b/packages/dataclass-editor/src/detail/field/entity/FieldEntityAssociation.tsx @@ -3,6 +3,7 @@ import { BasicCheckbox, BasicField, BasicSelect, Collapsible, CollapsibleContent import { useAppContext } from '../../../context/AppContext'; import { useEntityField } from '../../../context/FieldContext'; import { useMeta } from '../../../context/useMeta'; +import { updateCardinality } from '../../../data/dataclass-utils'; import './FieldEntityAssociation.css'; import { FieldEntityCascadeTypeCheckbox } from './FieldEntityCascadeTypeCheckbox'; import { useFieldEntityProperty } from './useFieldEntityProperty'; @@ -23,11 +24,7 @@ export const useCardinality = () => { const { field, setField } = useEntityField(); const setCardinality = (association: Association) => { const newField = structuredClone(field); - if (!association || association === 'MANY_TO_ONE') { - newField.entity.mappedByFieldName = ''; - newField.entity.orphanRemoval = false; - } - newField.entity.association = association; + updateCardinality(newField, association); setField(newField); }; return { cardinality: field.entity.association, setCardinality }; @@ -50,45 +47,47 @@ export const FieldEntityAssociation = () => { const possibleCardinalities = useMeta('meta/scripting/cardinalities', fieldContext, []).data; const cardinalities = cardinalityItems.filter(cardinality => possibleCardinalities.includes(cardinality.value)); - const mappedByFields = useMeta('meta/scripting/mappedByFields', fieldContext, []).data.map(mappedByField => ({ + const mappedByFields = useMeta('meta/scripting/mappedByFields', { ...fieldContext, cardinality }, []).data.map(mappedByField => ({ value: mappedByField, label: mappedByField })); return ( - - Association - - - - - - - - - - - - - - - - + Association + + + + + + + + + + + + + + + + + + setProperty('orphanRemoval', event.valueOf() as boolean)} disabled={mappedByFieldNameIsDisabled} /> - - setProperty('orphanRemoval', event.valueOf() as boolean)} - disabled={mappedByFieldNameIsDisabled} - /> - - - + + + + ) ); }; diff --git a/packages/protocol/src/types.ts b/packages/protocol/src/types.ts index ed9410f1..03287258 100644 --- a/packages/protocol/src/types.ts +++ b/packages/protocol/src/types.ts @@ -58,13 +58,15 @@ export interface ClientContext { client: Client; } +export type MappedByFieldsContext = FieldContext & { cardinality?: string }; + export interface MetaRequestTypes { 'meta/scripting/dataClasses': [DataClassEditorDataContext, Array]; 'meta/scripting/ivyTypes': [void, Array]; 'meta/scripting/allTypes': [TypeSearchRequest, Array]; 'meta/scripting/ownTypes': [TypeSearchRequest, Array]; 'meta/scripting/cardinalities': [FieldContext, Array]; - 'meta/scripting/mappedByFields': [FieldContext, Array]; + 'meta/scripting/mappedByFields': [MappedByFieldsContext, Array]; } export interface FunctionRequestTypes { diff --git a/playwright/tests/integration/mock/detail-dataclass-entity.spec.ts b/playwright/tests/integration/mock/detail-dataclass-entity.spec.ts index 8fb6663f..b240f0cd 100644 --- a/playwright/tests/integration/mock/detail-dataclass-entity.spec.ts +++ b/playwright/tests/integration/mock/detail-dataclass-entity.spec.ts @@ -46,7 +46,7 @@ describe('collapsible state', async () => { const entity = editor.detail.field.entity; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(1).locator.click(); + await editor.table.row(5).locator.click(); await entity.accordion.open(); await entity.databaseField.collapsible.expectToBeClosed(); @@ -111,23 +111,13 @@ describe('collapsible state', async () => { await editor.table.row(0).locator.click(); await entity.accordion.open(); - await entity.association.collapsible.expectToBeClosed(); - - await entity.association.fillValues( - 'One-to-One', - { - all: true, - persist: false, - merge: false, - remove: false, - refresh: false - }, - 'MappedByFieldName', - true - ); - await entity.accordion.reopen(); - - await entity.association.collapsible.expectToBeClosed(); + await expect(entity.association.collapsible.locator).toBeHidden(); + + await editor.table.row(5).locator.click(); + await entity.accordion.open(); + + await expect(entity.association.collapsible.locator).toBeVisible(); + await entity.association.collapsible.expectToBeOpen(); }); }); }); 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 85cf61db..d8b8d0b0 100644 --- a/playwright/tests/integration/mock/detail-field-entity-association.spec.ts +++ b/playwright/tests/integration/mock/detail-field-entity-association.spec.ts @@ -7,11 +7,21 @@ test.beforeEach(async ({ page }) => { editor = await DataClassEditor.openMock(page); }); +test('cardinality', async () => { + const cardinality = editor.detail.field.entity.association.cardinality; + + 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'); +}); + test('cascade', async () => { const association = editor.detail.field.entity.association; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(0).locator.click(); + await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await association.collapsible.open(); @@ -44,7 +54,7 @@ test('mapped by', async () => { const mappedBy = editor.detail.field.entity.association.mappedBy; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(0).locator.click(); + await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await editor.detail.field.entity.association.collapsible.open(); @@ -53,21 +63,27 @@ test('mapped by', async () => { await editor.detail.field.entity.association.cardinality.choose('One-to-One'); await expect(mappedBy.locator).toBeEnabled(); + await mappedBy.expectToHaveOptions('MappedByFieldName'); await editor.detail.field.entity.association.cardinality.choose('Many-to-One'); await expect(mappedBy.locator).toBeDisabled(); + await editor.table.row(6).locator.click(); + await editor.detail.field.entity.accordion.open(); + await editor.detail.field.entity.association.collapsible.open(); + await editor.detail.field.entity.association.cardinality.choose('One-to-Many'); await expect(mappedBy.locator).toBeEnabled(); + await mappedBy.expectToHaveOptions(); }); test('remove orphans', async () => { const removeOrphans = editor.detail.field.entity.association.removeOrphans; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(0).locator.click(); + await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await editor.detail.field.entity.association.collapsible.open(); @@ -81,6 +97,10 @@ test('remove orphans', async () => { await expect(removeOrphans).toBeDisabled(); + await editor.table.row(6).locator.click(); + await editor.detail.field.entity.accordion.open(); + await editor.detail.field.entity.association.collapsible.open(); + await editor.detail.field.entity.association.cardinality.choose('One-to-Many'); await expect(removeOrphans).toBeEnabled(); diff --git a/playwright/tests/integration/mock/detail-field-entity-database-field.spec.ts b/playwright/tests/integration/mock/detail-field-entity-database-field.spec.ts index 3f3538ef..158a423d 100644 --- a/playwright/tests/integration/mock/detail-field-entity-database-field.spec.ts +++ b/playwright/tests/integration/mock/detail-field-entity-database-field.spec.ts @@ -11,12 +11,12 @@ test('name', async () => { const databaseFieldName = editor.detail.field.entity.databaseField.name; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(1).locator.click(); + await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await editor.detail.field.entity.databaseField.collapsible.open(); await expect(databaseFieldName.locator).toHaveValue(''); - await databaseFieldName.expectToHavePlaceholder('firstName'); + await databaseFieldName.expectToHavePlaceholder('entity'); await expect(databaseFieldName.locator).toBeEnabled(); await databaseFieldName.locator.fill('DatabaseFieldName'); @@ -69,7 +69,7 @@ test('properties', async () => { const databaseField = editor.detail.field.entity.databaseField; await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.table.row(3).locator.click(); + await editor.table.row(5).locator.click(); await editor.detail.field.entity.accordion.open(); await databaseField.collapsible.open(); diff --git a/playwright/tests/integration/mock/master.spec.ts b/playwright/tests/integration/mock/master.spec.ts index 413e3b93..df4644a6 100644 --- a/playwright/tests/integration/mock/master.spec.ts +++ b/playwright/tests/integration/mock/master.spec.ts @@ -11,16 +11,15 @@ test.describe('add field', async () => { test.describe('add', async () => { test('data class', async () => { await editor.addField('newAttribute', 'String'); - await expect(editor.table.rows).toHaveCount(5); - await editor.table.row(4).expectToBeSelected(); - await editor.table.row(4).expectToHaveValues('newAttribute', 'String', ''); - await editor.table.row(4).locator.click(); + await expect(editor.table.rows).toHaveCount(7); + await editor.table.row(6).expectToBeSelected(); + await editor.table.row(6).expectToHaveValues('newAttribute', 'String', ''); await editor.detail.field.general.properties.expectToHaveValues(true); }); test('entity class', async () => { await editor.detail.dataClass.general.classType.fillValues('Entity'); - await editor.addField('newAttribute', 'String'); + await editor.addField('entityNew', 'String'); await editor.detail.field.general.properties.fillValues(true); await editor.detail.field.entity.accordion.open(); await editor.detail.field.entity.association.collapsible.open(); @@ -72,25 +71,25 @@ test.describe('add field', async () => { await expect(editor.add.locator).toBeVisible(); await editor.page.keyboard.press('Enter'); await expect(editor.add.locator).toBeHidden(); - await expect(editor.table.rows).toHaveCount(5); + await expect(editor.table.rows).toHaveCount(7); }); }); test.describe('delete field', async () => { test('delete', async () => { await editor.deleteField(1); - await expect(editor.table.rows).toHaveCount(3); + await expect(editor.table.rows).toHaveCount(5); await editor.table.row(1).expectToBeSelected(); await editor.detail.field.general.expectToHaveValues('date', 'Date', '', true, []); }); test('delete last field', async () => { - await editor.deleteField(3); - await expect(editor.table.rows).toHaveCount(3); + await editor.deleteField(5); + await expect(editor.table.rows).toHaveCount(5); - await editor.table.row(2).expectToBeSelected(); - await editor.detail.field.general.expectToHaveValues('date', 'Date', '', true, []); + await editor.table.row(4).expectToBeSelected(); + await editor.detail.field.general.expectToHaveValues('entity', 'mock.Entity', 'An entity.', true, []); }); test('delete last remaining field', async () => {