Skip to content

Commit cd89210

Browse files
committed
add side panel tests
1 parent b7eae99 commit cd89210

File tree

4 files changed

+241
-90
lines changed

4 files changed

+241
-90
lines changed

packages/compass-data-modeling/src/components/drawer/diagram-editor-side-panel.spec.tsx

Lines changed: 215 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
selectCollection,
1515
selectCurrentModelFromState,
1616
selectRelationship,
17+
selectField,
1718
} from '../../store/diagram';
1819
import dataModel from '../../../test/fixtures/data-model-with-relationships.json';
1920
import type {
@@ -41,6 +42,14 @@ async function comboboxSelectItem(
4142
});
4243
}
4344

45+
function getMultiComboboxValues(testId: string) {
46+
const combobox = screen.getByTestId(testId);
47+
expect(combobox).to.be.visible;
48+
return within(combobox)
49+
.getAllByRole('option')
50+
.map((option) => option.textContent);
51+
}
52+
4453
describe('DiagramEditorSidePanel', function () {
4554
before(function () {
4655
// TODO(COMPASS-9618): skip in electron runtime for now, drawer has issues rendering
@@ -144,6 +153,45 @@ describe('DiagramEditorSidePanel', function () {
144153
).to.be.visible;
145154
});
146155

156+
describe('Field context drawer', function () {
157+
it('should render a field context drawer when field is clicked', async function () {
158+
const result = renderDrawer();
159+
result.plugin.store.dispatch(selectField('flights.airlines', ['alias']));
160+
161+
await waitFor(() => {
162+
expect(screen.getByTitle('airlines.alias')).to.be.visible;
163+
});
164+
165+
const nameInput = screen.getByLabelText('Field name');
166+
expect(nameInput).to.be.visible;
167+
expect(nameInput).to.have.value('alias');
168+
169+
const selectedTypes = getMultiComboboxValues('lg-combobox-datatype');
170+
expect(selectedTypes).to.have.lengthOf(2);
171+
expect(selectedTypes).to.include('string');
172+
expect(selectedTypes).to.include('int');
173+
});
174+
175+
it('should render a nested field context drawer when field is clicked', async function () {
176+
const result = renderDrawer();
177+
result.plugin.store.dispatch(
178+
selectField('flights.routes', ['airline', 'id'])
179+
);
180+
181+
await waitFor(() => {
182+
expect(screen.getByTitle('routes.airline.id')).to.be.visible;
183+
});
184+
185+
const nameInput = screen.getByLabelText('Field name');
186+
expect(nameInput).to.be.visible;
187+
expect(nameInput).to.have.value('id');
188+
189+
const selectedTypes = getMultiComboboxValues('lg-combobox-datatype');
190+
expect(selectedTypes).to.have.lengthOf(1);
191+
expect(selectedTypes).to.include('string');
192+
});
193+
});
194+
147195
it('should change the content of the drawer when selecting different items', async function () {
148196
const result = renderDrawer();
149197

@@ -182,93 +230,185 @@ describe('DiagramEditorSidePanel', function () {
182230
expect(screen.getByLabelText('Name')).to.have.value('planes');
183231
});
184232

185-
it('should open and edit relationship starting from collection', async function () {
186-
const result = renderDrawer();
187-
result.plugin.store.dispatch(selectCollection('flights.countries'));
188-
189-
await waitFor(() => {
190-
expect(screen.getByLabelText('Name')).to.have.value('countries');
191-
});
233+
describe('Collection -> Relationships', function () {
234+
it('should add a relationship starting from a collection', async function () {
235+
const result = renderDrawer();
236+
result.plugin.store.dispatch(selectCollection('flights.countries'));
192237

193-
// Open relationshipt editing form
194-
const relationshipItem = screen
195-
.getByText('countries.name → airports.Country')
196-
.closest('li');
197-
expect(relationshipItem).to.be.visible;
198-
userEvent.click(
199-
within(relationshipItem!).getByRole('button', {
200-
name: 'Edit relationship',
201-
})
202-
);
203-
expect(screen.getByLabelText('Local field')).to.be.visible;
238+
await waitFor(() => {
239+
expect(screen.getByLabelText('Name')).to.have.value('countries');
240+
});
204241

205-
// Select new values
206-
await comboboxSelectItem('Local collection', 'planes');
207-
await comboboxSelectItem('Local field', 'name');
208-
await comboboxSelectItem('Foreign collection', 'countries');
209-
await comboboxSelectItem('Foreign field', 'iso_code');
242+
// Click on add relationship button
243+
userEvent.click(screen.getByRole('button', { name: 'Add relationship' }));
210244

211-
userEvent.click(screen.getByRole('textbox', { name: 'Notes' }));
212-
userEvent.type(
213-
screen.getByRole('textbox', { name: 'Notes' }),
214-
'Note about the relationship'
215-
);
216-
userEvent.tab();
245+
// Collection is pre-selected
246+
expect(screen.getByLabelText('Local collection')).to.be.visible;
247+
expect(screen.getByLabelText('Local collection')).to.have.value(
248+
'countries'
249+
);
250+
});
217251

218-
// We should be testing through rendered UI but as it's really hard to make
219-
// diagram rendering in tests property, we are just validating the final
220-
// model here
221-
const modifiedRelationship = selectCurrentModelFromState(
222-
result.plugin.store.getState()
223-
).relationships.find((r: Relationship) => {
224-
return r.id === '204b1fc0-601f-4d62-bba3-38fade71e049';
252+
it('should open and edit relationship starting from a collection', async function () {
253+
const result = renderDrawer();
254+
result.plugin.store.dispatch(selectCollection('flights.countries'));
255+
256+
await waitFor(() => {
257+
expect(screen.getByLabelText('Name')).to.have.value('countries');
258+
});
259+
260+
// Open relationshipt editing form
261+
const relationshipItem = screen
262+
.getByText('countries.name → airports.Country')
263+
.closest('li');
264+
expect(relationshipItem).to.be.visible;
265+
userEvent.click(
266+
within(relationshipItem!).getByRole('button', {
267+
name: 'Edit relationship',
268+
})
269+
);
270+
expect(screen.getByLabelText('Local field')).to.be.visible;
271+
272+
// Select new values
273+
await comboboxSelectItem('Local collection', 'planes');
274+
await comboboxSelectItem('Local field', 'name');
275+
await comboboxSelectItem('Foreign collection', 'countries');
276+
await comboboxSelectItem('Foreign field', 'iso_code');
277+
278+
userEvent.click(screen.getByRole('textbox', { name: 'Notes' }));
279+
userEvent.type(
280+
screen.getByRole('textbox', { name: 'Notes' }),
281+
'Note about the relationship'
282+
);
283+
userEvent.tab();
284+
285+
// We should be testing through rendered UI but as it's really hard to make
286+
// diagram rendering in tests property, we are just validating the final
287+
// model here
288+
const modifiedRelationship = selectCurrentModelFromState(
289+
result.plugin.store.getState()
290+
).relationships.find((r: Relationship) => {
291+
return r.id === '204b1fc0-601f-4d62-bba3-38fade71e049';
292+
});
293+
294+
expect(modifiedRelationship)
295+
.to.have.property('relationship')
296+
.deep.eq([
297+
{
298+
ns: 'flights.planes',
299+
fields: ['name'],
300+
cardinality: 1,
301+
},
302+
{
303+
ns: 'flights.countries',
304+
fields: ['iso_code'],
305+
cardinality: 100,
306+
},
307+
]);
308+
309+
expect(modifiedRelationship).to.have.property(
310+
'note',
311+
'Note about the relationship'
312+
);
225313
});
226314

227-
expect(modifiedRelationship)
228-
.to.have.property('relationship')
229-
.deep.eq([
230-
{
231-
ns: 'flights.planes',
232-
fields: ['name'],
233-
cardinality: 1,
234-
},
235-
{
236-
ns: 'flights.countries',
237-
fields: ['iso_code'],
238-
cardinality: 100,
239-
},
240-
]);
241-
242-
expect(modifiedRelationship).to.have.property(
243-
'note',
244-
'Note about the relationship'
245-
);
315+
it('should delete a relationship from a collection', async function () {
316+
const result = renderDrawer();
317+
result.plugin.store.dispatch(selectCollection('flights.countries'));
318+
319+
await waitFor(() => {
320+
expect(screen.getByLabelText('Name')).to.have.value('countries');
321+
});
322+
323+
// Find the relationhip item
324+
const relationshipItem = screen
325+
.getByText('countries.name → airports.Country')
326+
.closest('li');
327+
expect(relationshipItem).to.be.visible;
328+
329+
// Delete relationship
330+
userEvent.click(
331+
within(relationshipItem!).getByRole('button', {
332+
name: 'Delete relationship',
333+
})
334+
);
335+
336+
await waitFor(() => {
337+
expect(screen.queryByText('countries.name → airports.Country')).not.to
338+
.exist;
339+
});
340+
});
246341
});
247342

248-
it('should delete a relationship from collection', async function () {
249-
const result = renderDrawer();
250-
result.plugin.store.dispatch(selectCollection('flights.countries'));
343+
describe('Field -> Relationships', function () {
344+
it('should add a relationship starting from a field', async function () {
345+
const result = renderDrawer();
346+
result.plugin.store.dispatch(selectField('flights.countries', ['name']));
251347

252-
await waitFor(() => {
253-
expect(screen.getByLabelText('Name')).to.have.value('countries');
348+
await waitFor(() => {
349+
expect(screen.getByLabelText('Field name')).to.have.value('name');
350+
});
351+
352+
// Click on add relationship button
353+
userEvent.click(screen.getByRole('button', { name: 'Add relationship' }));
354+
355+
// Collection and field are pre-selected
356+
expect(screen.getByLabelText('Local collection')).to.be.visible;
357+
expect(screen.getByLabelText('Local collection')).to.have.value(
358+
'countries'
359+
);
360+
expect(screen.getByLabelText('Local field')).to.be.visible;
361+
expect(screen.getByLabelText('Local field')).to.have.value('name');
254362
});
255363

256-
// Find the relationhip item
257-
const relationshipItem = screen
258-
.getByText('countries.name → airports.Country')
259-
.closest('li');
260-
expect(relationshipItem).to.be.visible;
261-
262-
// Delete relationship
263-
userEvent.click(
264-
within(relationshipItem!).getByRole('button', {
265-
name: 'Delete relationship',
266-
})
267-
);
364+
it('should open a relationship starting from a field', async function () {
365+
const result = renderDrawer();
366+
result.plugin.store.dispatch(selectField('flights.countries', ['name']));
367+
368+
await waitFor(() => {
369+
expect(screen.getByLabelText('Field name')).to.have.value('name');
370+
});
371+
372+
// Open relationshipt editing form
373+
const relationshipItem = screen
374+
.getByText('airports.Country')
375+
.closest('li');
376+
expect(relationshipItem).to.be.visible;
377+
userEvent.click(
378+
within(relationshipItem!).getByRole('button', {
379+
name: 'Edit relationship',
380+
})
381+
);
382+
expect(screen.getByLabelText('Local field')).to.be.visible;
383+
expect(screen.getByLabelText('Local field')).to.have.value('name');
384+
expect(screen.getByLabelText('Foreign field')).to.be.visible;
385+
expect(screen.getByLabelText('Foreign field')).to.have.value('Country');
386+
});
268387

269-
await waitFor(() => {
270-
expect(screen.queryByText('countries.name → airports.Country')).not.to
271-
.exist;
388+
it('should delete a relationship from a field', async function () {
389+
const result = renderDrawer();
390+
result.plugin.store.dispatch(selectField('flights.countries', ['name']));
391+
392+
await waitFor(() => {
393+
expect(screen.getByLabelText('Field name')).to.have.value('name');
394+
});
395+
396+
// Find the relationhip item
397+
const relationshipItem = screen
398+
.getByText('airports.Country')
399+
.closest('li');
400+
expect(relationshipItem).to.be.visible;
401+
402+
// Delete relationship
403+
userEvent.click(
404+
within(relationshipItem!).getByRole('button', {
405+
name: 'Delete relationship',
406+
})
407+
);
408+
409+
await waitFor(() => {
410+
expect(screen.queryByText('airports.Country')).not.to.exist;
411+
});
272412
});
273413
});
274414

packages/compass-data-modeling/src/components/drawer/diagram-editor-side-panel.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import { getDefaultRelationshipName } from '../../utils';
1919
import FieldDrawerContent from './field-drawer-content';
2020
import type { FieldPath } from '../../services/data-model-storage';
21+
import { getFieldFromSchema } from '../../utils/schema-traversal';
2122

2223
export const DATA_MODELING_DRAWER_ID = 'data-modeling-drawer';
2324

@@ -213,16 +214,19 @@ export default connect(
213214
}
214215

215216
if (selected.type === 'field') {
216-
// const collection = model.collections.find((collection) => (collection.ns === selected.namespace));
217-
// const field =
218-
219-
// if (!field) {
220-
// // TODO(COMPASS-9680): When the selected field doesn't exist we don't
221-
// // show any selection.
222-
// return {
223-
// selectedItems: null,
224-
// };
225-
// }
217+
// TODO(COMPASS-9680): Can be cleaned up after COMPASS-9680 is done (the selection updates with undo/redo)
218+
const collection = model.collections.find(
219+
(collection) => collection.ns === selected.namespace
220+
);
221+
const field = getFieldFromSchema({
222+
jsonSchema: collection?.jsonSchema ?? {},
223+
fieldPath: selected.fieldPath,
224+
});
225+
if (!field) {
226+
return {
227+
selectedItems: null,
228+
};
229+
}
226230

227231
return {
228232
selectedItems: {
@@ -237,6 +241,6 @@ export default connect(
237241
{
238242
onDeleteCollection: deleteCollection,
239243
onDeleteRelationship: deleteRelationship,
240-
onDeleteField: () => {}, // TODO onDeleteField,
244+
onDeleteField: () => {}, // TODO(COMPASS-9659) part 2 - implement onDeleteField,
241245
}
242246
)(DiagramEditorSidePanel);

packages/compass-data-modeling/src/components/drawer/field-drawer-content.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ const FieldDrawerContent: React.FunctionComponent<FieldDrawerContentProps> = ({
144144

145145
<DMFormFieldContainer>
146146
<Combobox
147+
data-testid="lg-combobox-datatype"
148+
label="Datatype"
147149
aria-label="Datatype"
148150
disabled={true} // TODO: enable when field type change is implemented
149151
value={types}

0 commit comments

Comments
 (0)