mirror of
https://github.com/nasa/openmct.git
synced 2025-06-17 06:38:17 +00:00
* New forms code needs tests #4539 Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com> Co-authored-by: unlikelyzero <jchill2@gmail.com> Co-authored-by: Joshi <simplyrender@gmail.com>
This commit is contained in:
79
e2e/tests/api/forms/forms.e2e.spec.js
Normal file
79
e2e/tests/api/forms/forms.e2e.spec.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache 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
|
||||||
|
* http://www.apache.org/licenses/LICENSE-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.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This test suite is dedicated to tests which verify form functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { test, expect } = require('@playwright/test');
|
||||||
|
|
||||||
|
const TEST_FOLDER = 'test folder';
|
||||||
|
|
||||||
|
test.describe('forms set', () => {
|
||||||
|
test('New folder form has title as required field', async ({ page }) => {
|
||||||
|
//Go to baseURL
|
||||||
|
await page.goto('/', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
// Click button:has-text("Create")
|
||||||
|
await page.click('button:has-text("Create")');
|
||||||
|
// Click :nth-match(:text("Folder"), 2)
|
||||||
|
await page.click(':nth-match(:text("Folder"), 2)');
|
||||||
|
// Click text=Properties Title Notes >> input[type="text"]
|
||||||
|
await page.click('text=Properties Title Notes >> input[type="text"]');
|
||||||
|
// Fill text=Properties Title Notes >> input[type="text"]
|
||||||
|
await page.fill('text=Properties Title Notes >> input[type="text"]', '');
|
||||||
|
// Press Tab
|
||||||
|
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
|
||||||
|
// Click text=OK Cancel
|
||||||
|
await page.click('text=OK', { force: true });
|
||||||
|
|
||||||
|
const okButton = page.locator('text=OK');
|
||||||
|
|
||||||
|
await expect(okButton).toBeDisabled();
|
||||||
|
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
|
||||||
|
|
||||||
|
// Click text=Properties Title Notes >> input[type="text"]
|
||||||
|
await page.click('text=Properties Title Notes >> input[type="text"]');
|
||||||
|
// Fill text=Properties Title Notes >> input[type="text"]
|
||||||
|
await page.fill('text=Properties Title Notes >> input[type="text"]', TEST_FOLDER);
|
||||||
|
// Press Tab
|
||||||
|
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
|
||||||
|
|
||||||
|
await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
|
||||||
|
|
||||||
|
// Click text=OK
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation(),
|
||||||
|
page.click('text=OK')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
|
||||||
|
});
|
||||||
|
test.fixme('Create all object types and verify correctness', async ({ page }) => {
|
||||||
|
//Create the following Domain Objects with their unique Object Types
|
||||||
|
// Sine Wave Generator (number object)
|
||||||
|
// Timer Object
|
||||||
|
// Plan View Object
|
||||||
|
// Clock Object
|
||||||
|
// Hyperlink
|
||||||
|
});
|
||||||
|
});
|
@ -23,10 +23,13 @@
|
|||||||
import FormController from './FormController';
|
import FormController from './FormController';
|
||||||
import FormProperties from './components/FormProperties.vue';
|
import FormProperties from './components/FormProperties.vue';
|
||||||
|
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default class FormsAPI {
|
export default class FormsAPI extends EventEmitter {
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.formController = new FormController(openmct);
|
this.formController = new FormController(openmct);
|
||||||
}
|
}
|
||||||
@ -107,6 +110,8 @@ export default class FormsAPI {
|
|||||||
let onDismiss;
|
let onDismiss;
|
||||||
let onSave;
|
let onSave;
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
onSave = onFormSave(resolve);
|
onSave = onFormSave(resolve);
|
||||||
onDismiss = onFormDismiss(reject);
|
onDismiss = onFormDismiss(reject);
|
||||||
@ -115,7 +120,7 @@ export default class FormsAPI {
|
|||||||
const vm = new Vue({
|
const vm = new Vue({
|
||||||
components: { FormProperties },
|
components: { FormProperties },
|
||||||
provide: {
|
provide: {
|
||||||
openmct: this.openmct
|
openmct: self.openmct
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -132,7 +137,7 @@ export default class FormsAPI {
|
|||||||
if (element) {
|
if (element) {
|
||||||
element.append(formElement);
|
element.append(formElement);
|
||||||
} else {
|
} else {
|
||||||
overlay = this.openmct.overlays.overlay({
|
overlay = self.openmct.overlays.overlay({
|
||||||
element: vm.$el,
|
element: vm.$el,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
onDestroy: () => vm.$destroy()
|
onDestroy: () => vm.$destroy()
|
||||||
@ -140,6 +145,7 @@ export default class FormsAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFormPropertyChange(data) {
|
function onFormPropertyChange(data) {
|
||||||
|
self.emit('onFormPropertyChange', data);
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(data);
|
onChange(data);
|
||||||
}
|
}
|
||||||
|
157
src/api/forms/FormsAPISpec.js
Normal file
157
src/api/forms/FormsAPISpec.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache 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
|
||||||
|
* http://www.apache.org/licenses/LICENSE-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.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import { createOpenMct, resetApplicationState } from '../../utils/testing';
|
||||||
|
|
||||||
|
describe('The Forms API', () => {
|
||||||
|
let openmct;
|
||||||
|
let element;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
element = document.createElement('div');
|
||||||
|
element.style.display = 'block';
|
||||||
|
element.style.width = '1920px';
|
||||||
|
element.style.height = '1080px';
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.on('start', done);
|
||||||
|
|
||||||
|
openmct.startHeadless(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('openmct supports form API', () => {
|
||||||
|
expect(openmct.forms).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('check default form controls exists', () => {
|
||||||
|
it('autocomplete', () => {
|
||||||
|
const control = openmct.forms.getFormControl('autocomplete');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clock', () => {
|
||||||
|
const control = openmct.forms.getFormControl('composite');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('datetime', () => {
|
||||||
|
const control = openmct.forms.getFormControl('datetime');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('file-input', () => {
|
||||||
|
const control = openmct.forms.getFormControl('file-input');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('locator', () => {
|
||||||
|
const control = openmct.forms.getFormControl('locator');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('numberfield', () => {
|
||||||
|
const control = openmct.forms.getFormControl('numberfield');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('select', () => {
|
||||||
|
const control = openmct.forms.getFormControl('select');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('textarea', () => {
|
||||||
|
const control = openmct.forms.getFormControl('textarea');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('textfield', () => {
|
||||||
|
const control = openmct.forms.getFormControl('textfield');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports user defined form controls', () => {
|
||||||
|
const newFormControl = {
|
||||||
|
show: () => {
|
||||||
|
console.log('show new control');
|
||||||
|
},
|
||||||
|
destroy: () => {
|
||||||
|
console.log('destroy');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.forms.addNewFormControl('newFormControl', newFormControl);
|
||||||
|
const control = openmct.forms.getFormControl('newFormControl');
|
||||||
|
expect(control).not.toBe(null);
|
||||||
|
expect(control.show).not.toBe(null);
|
||||||
|
expect(control.destroy).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('show form on UI', () => {
|
||||||
|
let formStructure;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
formStructure = {
|
||||||
|
title: 'Test Show Form',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
control: 'textfield',
|
||||||
|
name: 'Title',
|
||||||
|
pattern: '\\S+',
|
||||||
|
required: false,
|
||||||
|
cssClass: 'l-input-lg',
|
||||||
|
value: 'Test Name'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when container element is provided', (done) => {
|
||||||
|
openmct.forms.showForm(formStructure, { element }).catch(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
const titleElement = element.querySelector('.c-overlay__dialog-title');
|
||||||
|
expect(titleElement.textContent).toBe(formStructure.title);
|
||||||
|
|
||||||
|
element.querySelector('.js-cancel-button').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when container element is not provided', (done) => {
|
||||||
|
openmct.forms.showForm(formStructure).catch(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const titleElement = document.querySelector('.c-overlay__dialog-title');
|
||||||
|
const title = titleElement.textContent;
|
||||||
|
|
||||||
|
expect(title).toBe(formStructure.title);
|
||||||
|
document.querySelector('.js-cancel-button').click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -21,9 +21,9 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-form">
|
<div class="c-form js-form">
|
||||||
<div class="c-overlay__top-bar c-form__top-bar">
|
<div class="c-overlay__top-bar c-form__top-bar">
|
||||||
<div class="c-overlay__dialog-title">{{ model.title }}</div>
|
<div class="c-overlay__dialog-title js-form-title">{{ model.title }}</div>
|
||||||
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||||
</div>
|
</div>
|
||||||
<form
|
<form
|
||||||
@ -70,7 +70,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="c-button"
|
class="c-button js-cancel-button"
|
||||||
@click="onDismiss"
|
@click="onDismiss"
|
||||||
>
|
>
|
||||||
{{ cancelLabel }}
|
{{ cancelLabel }}
|
||||||
|
@ -26,29 +26,31 @@ import { createOpenMct, createMouseEvent, resetApplicationState } from '../../ut
|
|||||||
|
|
||||||
describe ('The Menu API', () => {
|
describe ('The Menu API', () => {
|
||||||
let openmct;
|
let openmct;
|
||||||
let element;
|
let appHolder;
|
||||||
let menuAPI;
|
let menuAPI;
|
||||||
let actionsArray;
|
let actionsArray;
|
||||||
let x;
|
|
||||||
let y;
|
|
||||||
let result;
|
let result;
|
||||||
let onDestroy;
|
let menuElement;
|
||||||
|
|
||||||
|
const x = 8;
|
||||||
|
const y = 16;
|
||||||
|
|
||||||
|
const menuOptions = {
|
||||||
|
onDestroy: () => {
|
||||||
|
console.log('default onDestroy');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
const appHolder = document.createElement('div');
|
appHolder = document.createElement('div');
|
||||||
appHolder.style.display = 'block';
|
appHolder.style.display = 'block';
|
||||||
appHolder.style.width = '1920px';
|
appHolder.style.width = '1920px';
|
||||||
appHolder.style.height = '1080px';
|
appHolder.style.height = '1080px';
|
||||||
|
|
||||||
openmct = createOpenMct();
|
openmct = createOpenMct();
|
||||||
|
|
||||||
element = document.createElement('div');
|
|
||||||
element.style.display = 'block';
|
|
||||||
element.style.width = '1920px';
|
|
||||||
element.style.height = '1080px';
|
|
||||||
|
|
||||||
openmct.on('start', done);
|
openmct.on('start', done);
|
||||||
openmct.startHeadless(appHolder);
|
openmct.startHeadless();
|
||||||
|
|
||||||
menuAPI = new MenuAPI(openmct);
|
menuAPI = new MenuAPI(openmct);
|
||||||
actionsArray = [
|
actionsArray = [
|
||||||
@ -56,7 +58,7 @@ describe ('The Menu API', () => {
|
|||||||
key: 'test-css-class-1',
|
key: 'test-css-class-1',
|
||||||
name: 'Test Action 1',
|
name: 'Test Action 1',
|
||||||
cssClass: 'icon-clock',
|
cssClass: 'icon-clock',
|
||||||
description: 'This is a test action',
|
description: 'This is a test action 1',
|
||||||
onItemClicked: () => {
|
onItemClicked: () => {
|
||||||
result = 'Test Action 1 Invoked';
|
result = 'Test Action 1 Invoked';
|
||||||
}
|
}
|
||||||
@ -65,149 +67,165 @@ describe ('The Menu API', () => {
|
|||||||
key: 'test-css-class-2',
|
key: 'test-css-class-2',
|
||||||
name: 'Test Action 2',
|
name: 'Test Action 2',
|
||||||
cssClass: 'icon-clock',
|
cssClass: 'icon-clock',
|
||||||
description: 'This is a test action',
|
description: 'This is a test action 2',
|
||||||
onItemClicked: () => {
|
onItemClicked: () => {
|
||||||
result = 'Test Action 2 Invoked';
|
result = 'Test Action 2 Invoked';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
x = 8;
|
|
||||||
y = 16;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
return resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("showMenu method", () => {
|
describe('showMenu method', () => {
|
||||||
it("creates an instance of Menu when invoked", () => {
|
beforeAll(() => {
|
||||||
menuAPI.showMenu(x, y, actionsArray);
|
spyOn(menuOptions, 'onDestroy').and.callThrough();
|
||||||
|
|
||||||
expect(menuAPI.menuComponent).toBeInstanceOf(Menu);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("creates a menu component", () => {
|
it('creates an instance of Menu when invoked', (done) => {
|
||||||
let menuComponent;
|
menuOptions.onDestroy = done;
|
||||||
let vueComponent;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
onDestroy = jasmine.createSpy('onDestroy');
|
|
||||||
|
|
||||||
const menuOptions = {
|
|
||||||
onDestroy
|
|
||||||
};
|
|
||||||
|
|
||||||
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
vueComponent = menuAPI.menuComponent.component;
|
|
||||||
menuComponent = document.querySelector(".c-menu");
|
|
||||||
|
|
||||||
spyOn(vueComponent, '$destroy');
|
expect(menuAPI.menuComponent).toBeInstanceOf(Menu);
|
||||||
|
document.body.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders a menu component in the expected x and y coordinates", () => {
|
describe('creates a menu component', () => {
|
||||||
let boundingClientRect = menuComponent.getBoundingClientRect();
|
it('with all the actions passed in', (done) => {
|
||||||
let left = boundingClientRect.left;
|
menuOptions.onDestroy = done;
|
||||||
let top = boundingClientRect.top;
|
|
||||||
|
|
||||||
expect(left).toEqual(x);
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
expect(top).toEqual(y);
|
menuElement = document.querySelector('.c-menu');
|
||||||
});
|
expect(menuElement).toBeDefined();
|
||||||
|
|
||||||
it("with all the actions passed in", () => {
|
const listItems = menuElement.children[0].children;
|
||||||
expect(menuComponent).toBeDefined();
|
|
||||||
|
|
||||||
let listItems = menuComponent.children[0].children;
|
|
||||||
|
|
||||||
expect(listItems.length).toEqual(actionsArray.length);
|
expect(listItems.length).toEqual(actionsArray.length);
|
||||||
|
document.body.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("with click-able menu items, that will invoke the correct callBacks", () => {
|
it('with click-able menu items, that will invoke the correct callBack', (done) => {
|
||||||
let listItem1 = menuComponent.children[0].children[0];
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
|
||||||
|
menuElement = document.querySelector('.c-menu');
|
||||||
|
const listItem1 = menuElement.children[0].children[0];
|
||||||
|
|
||||||
listItem1.click();
|
listItem1.click();
|
||||||
|
|
||||||
expect(result).toEqual("Test Action 1 Invoked");
|
expect(result).toEqual('Test Action 1 Invoked');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("dismisses the menu when action is clicked on", () => {
|
it('dismisses the menu when action is clicked on', (done) => {
|
||||||
let listItem1 = menuComponent.children[0].children[0];
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
|
||||||
|
menuElement = document.querySelector('.c-menu');
|
||||||
|
const listItem1 = menuElement.children[0].children[0];
|
||||||
listItem1.click();
|
listItem1.click();
|
||||||
|
|
||||||
let menu = document.querySelector('.c-menu');
|
menuElement = document.querySelector('.c-menu');
|
||||||
|
|
||||||
expect(menu).toBeNull();
|
expect(menuElement).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes the destroy method when menu is dismissed", () => {
|
it('invokes the destroy method when menu is dismissed', (done) => {
|
||||||
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
|
||||||
|
const vueComponent = menuAPI.menuComponent.component;
|
||||||
|
spyOn(vueComponent, '$destroy');
|
||||||
|
|
||||||
document.body.click();
|
document.body.click();
|
||||||
|
|
||||||
expect(vueComponent.$destroy).toHaveBeenCalled();
|
expect(vueComponent.$destroy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes the onDestroy callback if passed in", () => {
|
it('invokes the onDestroy callback if passed in', (done) => {
|
||||||
|
let count = 0;
|
||||||
|
menuOptions.onDestroy = () => {
|
||||||
|
count++;
|
||||||
|
expect(count).toEqual(1);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
|
||||||
document.body.click();
|
document.body.click();
|
||||||
|
|
||||||
expect(onDestroy).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("superMenu method", () => {
|
describe('superMenu method', () => {
|
||||||
it("creates a superMenu", () => {
|
it('creates a superMenu', (done) => {
|
||||||
menuAPI.showSuperMenu(x, y, actionsArray);
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
const superMenu = document.querySelector('.c-super-menu__menu');
|
menuAPI.showSuperMenu(x, y, actionsArray, menuOptions);
|
||||||
|
menuElement = document.querySelector('.c-super-menu__menu');
|
||||||
|
|
||||||
expect(superMenu).not.toBeNull();
|
expect(menuElement).not.toBeNull();
|
||||||
|
document.body.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Mouse over a superMenu shows correct description", (done) => {
|
it('Mouse over a superMenu shows correct description', (done) => {
|
||||||
menuAPI.showSuperMenu(x, y, actionsArray);
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
const superMenu = document.querySelector('.c-super-menu__menu');
|
menuAPI.showSuperMenu(x, y, actionsArray, menuOptions);
|
||||||
const superMenuItem = superMenu.querySelector('li');
|
menuElement = document.querySelector('.c-super-menu__menu');
|
||||||
|
|
||||||
|
const superMenuItem = menuElement.querySelector('li');
|
||||||
const mouseOverEvent = createMouseEvent('mouseover');
|
const mouseOverEvent = createMouseEvent('mouseover');
|
||||||
|
|
||||||
superMenuItem.dispatchEvent(mouseOverEvent);
|
superMenuItem.dispatchEvent(mouseOverEvent);
|
||||||
const itemDescription = document.querySelector('.l-item-description__description');
|
const itemDescription = document.querySelector('.l-item-description__description');
|
||||||
|
|
||||||
setTimeout(() => {
|
menuAPI.menuComponent.component.$nextTick(() => {
|
||||||
|
expect(menuElement).not.toBeNull();
|
||||||
expect(itemDescription.innerText).toEqual(actionsArray[0].description);
|
expect(itemDescription.innerText).toEqual(actionsArray[0].description);
|
||||||
expect(superMenu).not.toBeNull();
|
|
||||||
done();
|
document.body.click();
|
||||||
}, 300);
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Menu Placements", () => {
|
describe('Menu Placements', () => {
|
||||||
it("default menu position BOTTOM_RIGHT", () => {
|
it('default menu position BOTTOM_RIGHT', (done) => {
|
||||||
menuAPI.showMenu(x, y, actionsArray);
|
menuOptions.onDestroy = done;
|
||||||
|
|
||||||
const menu = document.querySelector('.c-menu');
|
|
||||||
|
|
||||||
const boundingClientRect = menu.getBoundingClientRect();
|
|
||||||
const left = boundingClientRect.left;
|
|
||||||
const top = boundingClientRect.top;
|
|
||||||
|
|
||||||
expect(left).toEqual(x);
|
|
||||||
expect(top).toEqual(y);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("menu position BOTTOM_RIGHT", () => {
|
|
||||||
const menuOptions = {
|
|
||||||
placement: openmct.menus.menuPlacement.BOTTOM_RIGHT
|
|
||||||
};
|
|
||||||
|
|
||||||
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
menuElement = document.querySelector('.c-menu');
|
||||||
|
|
||||||
const menu = document.querySelector('.c-menu');
|
const boundingClientRect = menuElement.getBoundingClientRect();
|
||||||
const boundingClientRect = menu.getBoundingClientRect();
|
|
||||||
const left = boundingClientRect.left;
|
const left = boundingClientRect.left;
|
||||||
const top = boundingClientRect.top;
|
const top = boundingClientRect.top;
|
||||||
|
|
||||||
expect(left).toEqual(x);
|
expect(left).toEqual(x);
|
||||||
expect(top).toEqual(y);
|
expect(top).toEqual(y);
|
||||||
|
|
||||||
|
document.body.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('menu position BOTTOM_RIGHT', (done) => {
|
||||||
|
menuOptions.onDestroy = done;
|
||||||
|
menuOptions.placement = openmct.menus.menuPlacement.BOTTOM_RIGHT;
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
menuElement = document.querySelector('.c-menu');
|
||||||
|
|
||||||
|
const boundingClientRect = menuElement.getBoundingClientRect();
|
||||||
|
const left = boundingClientRect.left;
|
||||||
|
const top = boundingClientRect.top;
|
||||||
|
|
||||||
|
expect(left).toEqual(x);
|
||||||
|
expect(top).toEqual(y);
|
||||||
|
|
||||||
|
document.body.click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -40,11 +40,13 @@ describe("The User API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
const activeOverlays = openmct.overlays.activeOverlays;
|
||||||
|
activeOverlays.forEach(overlay => overlay.dismiss());
|
||||||
|
|
||||||
return resetApplicationState(openmct);
|
return resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with regard to user providers', () => {
|
describe('with regard to user providers', () => {
|
||||||
|
|
||||||
it('allows you to specify a user provider', () => {
|
it('allows you to specify a user provider', () => {
|
||||||
openmct.user.on('providerAdded', (provider) => {
|
openmct.user.on('providerAdded', (provider) => {
|
||||||
expect(provider).toBeInstanceOf(ExampleUserProvider);
|
expect(provider).toBeInstanceOf(ExampleUserProvider);
|
||||||
|
128
src/plugins/formActions/CreateActionSpec.js
Normal file
128
src/plugins/formActions/CreateActionSpec.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache 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
|
||||||
|
* http://www.apache.org/licenses/LICENSE-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.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import CreateAction from './CreateAction';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
let parentObject;
|
||||||
|
let parentObjectPath;
|
||||||
|
let unObserve;
|
||||||
|
|
||||||
|
describe("The create action plugin", () => {
|
||||||
|
let openmct;
|
||||||
|
|
||||||
|
const TYPES = [
|
||||||
|
'clock',
|
||||||
|
'conditionWidget',
|
||||||
|
'conditionWidget',
|
||||||
|
'example.imagery',
|
||||||
|
'example.state-generator',
|
||||||
|
'flexible-layout',
|
||||||
|
'folder',
|
||||||
|
'generator',
|
||||||
|
'hyperlink',
|
||||||
|
'LadTable',
|
||||||
|
'LadTableSet',
|
||||||
|
'layout',
|
||||||
|
'mmgis',
|
||||||
|
'notebook',
|
||||||
|
'plan',
|
||||||
|
'table',
|
||||||
|
'tabs',
|
||||||
|
'telemetry-mean',
|
||||||
|
'telemetry.plot.bar-graph',
|
||||||
|
'telemetry.plot.overlay',
|
||||||
|
'telemetry.plot.stacked',
|
||||||
|
'time-strip',
|
||||||
|
'timer',
|
||||||
|
'webpage'
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('creates new objects for a', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
parentObject = {
|
||||||
|
name: 'mock folder',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
parentObjectPath = [parentObject];
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'save');
|
||||||
|
openmct.objects.save.and.callThrough();
|
||||||
|
spyOn(openmct.forms, 'showForm');
|
||||||
|
openmct.forms.showForm.and.callFake(formStructure => {
|
||||||
|
return Promise.resolve({
|
||||||
|
name: 'test',
|
||||||
|
notes: 'test notes',
|
||||||
|
location: parentObjectPath
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
parentObject = null;
|
||||||
|
unObserve();
|
||||||
|
});
|
||||||
|
|
||||||
|
TYPES.forEach(type => {
|
||||||
|
it(`type ${type}`, (done) => {
|
||||||
|
function callback(newObject) {
|
||||||
|
const composition = newObject.composition;
|
||||||
|
|
||||||
|
openmct.objects.get(composition[0])
|
||||||
|
.then(object => {
|
||||||
|
expect(object.type).toEqual(type);
|
||||||
|
expect(object.location).toEqual(openmct.objects.makeKeyString(parentObject.identifier));
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const deBouncedCallback = debounce(callback, 300);
|
||||||
|
unObserve = openmct.objects.observe(parentObject, '*', deBouncedCallback);
|
||||||
|
|
||||||
|
const createAction = new CreateAction(openmct, type, parentObject);
|
||||||
|
createAction.invoke();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -45,7 +45,7 @@ export default class EditPropertiesAction extends PropertiesAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoke(objectPath) {
|
invoke(objectPath) {
|
||||||
this._showEditForm(objectPath);
|
return this._showEditForm(objectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +86,7 @@ export default class EditPropertiesAction extends PropertiesAction {
|
|||||||
const formStructure = createWizard.getFormStructure(false);
|
const formStructure = createWizard.getFormStructure(false);
|
||||||
formStructure.title = 'Edit ' + this.domainObject.name;
|
formStructure.title = 'Edit ' + this.domainObject.name;
|
||||||
|
|
||||||
this.openmct.forms.showForm(formStructure)
|
return this.openmct.forms.showForm(formStructure)
|
||||||
.then(this._onSave.bind(this));
|
.then(this._onSave.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
222
src/plugins/formActions/pluginSpec.js
Normal file
222
src/plugins/formActions/pluginSpec.js
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache 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
|
||||||
|
* http://www.apache.org/licenses/LICENSE-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.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import {
|
||||||
|
createMouseEvent,
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
describe('EditPropertiesAction plugin', () => {
|
||||||
|
let editPropertiesAction;
|
||||||
|
let openmct;
|
||||||
|
let element;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
element = document.createElement('div');
|
||||||
|
element.style.display = 'block';
|
||||||
|
element.style.width = '1920px';
|
||||||
|
element.style.height = '1080px';
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless(element);
|
||||||
|
|
||||||
|
editPropertiesAction = openmct.actions.getAction('properties');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
editPropertiesAction = null;
|
||||||
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('editPropertiesAction exists', () => {
|
||||||
|
expect(editPropertiesAction.key).toEqual('properties');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edit properties action applies to only persistable objects', () => {
|
||||||
|
spyOn(openmct.objects, 'isPersistable').and.returnValue(true);
|
||||||
|
|
||||||
|
const domainObject = {
|
||||||
|
name: 'mock folder',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
|
||||||
|
expect(isApplicableTo).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edit properties action does not apply to non persistable objects', () => {
|
||||||
|
spyOn(openmct.objects, 'isPersistable').and.returnValue(false);
|
||||||
|
|
||||||
|
const domainObject = {
|
||||||
|
name: 'mock folder',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
|
||||||
|
expect(isApplicableTo).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edit properties action when invoked shows form', (done) => {
|
||||||
|
const domainObject = {
|
||||||
|
name: 'mock folder',
|
||||||
|
notes: 'mock notes',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
modified: 1643065068597,
|
||||||
|
persisted: 1643065068600,
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const deBouncedFormChange = debounce(handleFormPropertyChange, 500);
|
||||||
|
openmct.forms.on('onFormPropertyChange', deBouncedFormChange);
|
||||||
|
|
||||||
|
function handleFormPropertyChange(data) {
|
||||||
|
const form = document.querySelector('.js-form');
|
||||||
|
const title = form.querySelector('input');
|
||||||
|
expect(title.value).toEqual(domainObject.name);
|
||||||
|
|
||||||
|
const notes = form.querySelector('textArea');
|
||||||
|
expect(notes.value).toEqual(domainObject.notes);
|
||||||
|
|
||||||
|
const buttons = form.querySelectorAll('button');
|
||||||
|
expect(buttons[0].textContent.trim()).toEqual('OK');
|
||||||
|
expect(buttons[1].textContent.trim()).toEqual('Cancel');
|
||||||
|
|
||||||
|
const clickEvent = createMouseEvent('click');
|
||||||
|
buttons[1].dispatchEvent(clickEvent);
|
||||||
|
|
||||||
|
openmct.forms.off('onFormPropertyChange', deBouncedFormChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
editPropertiesAction.invoke([domainObject])
|
||||||
|
.catch(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edit properties action saves changes', (done) => {
|
||||||
|
const oldName = 'mock folder';
|
||||||
|
const newName = 'renamed mock folder';
|
||||||
|
const domainObject = {
|
||||||
|
name: oldName,
|
||||||
|
notes: 'mock notes',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
modified: 1643065068597,
|
||||||
|
persisted: 1643065068600,
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
let unObserve;
|
||||||
|
|
||||||
|
function callback(newObject) {
|
||||||
|
expect(newObject.name).not.toEqual(oldName);
|
||||||
|
expect(newObject.name).toEqual(newName);
|
||||||
|
|
||||||
|
unObserve();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
const deBouncedCallback = debounce(callback, 300);
|
||||||
|
unObserve = openmct.objects.observe(domainObject, '*', deBouncedCallback);
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
const deBouncedFormChange = debounce(handleFormPropertyChange, 500);
|
||||||
|
openmct.forms.on('onFormPropertyChange', deBouncedFormChange);
|
||||||
|
|
||||||
|
function handleFormPropertyChange(data) {
|
||||||
|
const form = document.querySelector('.js-form');
|
||||||
|
const title = form.querySelector('input');
|
||||||
|
const notes = form.querySelector('textArea');
|
||||||
|
|
||||||
|
const buttons = form.querySelectorAll('button');
|
||||||
|
expect(buttons[0].textContent.trim()).toEqual('OK');
|
||||||
|
expect(buttons[1].textContent.trim()).toEqual('Cancel');
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
expect(title.value).toEqual(domainObject.name);
|
||||||
|
expect(notes.value).toEqual(domainObject.notes);
|
||||||
|
|
||||||
|
// change input field value and dispatch event for it
|
||||||
|
title.focus();
|
||||||
|
title.value = newName;
|
||||||
|
title.dispatchEvent(new Event('input'));
|
||||||
|
title.blur();
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
// click ok to save form changes
|
||||||
|
const clickEvent = createMouseEvent('click');
|
||||||
|
buttons[0].dispatchEvent(clickEvent);
|
||||||
|
|
||||||
|
openmct.forms.off('onFormPropertyChange', deBouncedFormChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editPropertiesAction.invoke([domainObject]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('edit properties action discards changes', (done) => {
|
||||||
|
const name = 'mock folder';
|
||||||
|
const domainObject = {
|
||||||
|
name,
|
||||||
|
notes: 'mock notes',
|
||||||
|
type: 'folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
modified: 1643065068597,
|
||||||
|
persisted: 1643065068600,
|
||||||
|
composition: []
|
||||||
|
};
|
||||||
|
|
||||||
|
editPropertiesAction.invoke([domainObject])
|
||||||
|
.catch(() => {
|
||||||
|
expect(domainObject.name).toEqual(name);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = document.querySelector('.js-form');
|
||||||
|
const buttons = form.querySelectorAll('button');
|
||||||
|
const clickEvent = createMouseEvent('click');
|
||||||
|
buttons[1].dispatchEvent(clickEvent);
|
||||||
|
});
|
||||||
|
});
|
@ -91,6 +91,7 @@ describe("The Move Action plugin", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when moving an object to a new parent and removing from the old parent", () => {
|
describe("when moving an object to a new parent and removing from the old parent", () => {
|
||||||
|
let unObserve;
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
openmct.router.path = [];
|
openmct.router.path = [];
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ describe("The Move Action plugin", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
openmct.objects.observe(parentObject, '*', (newObject) => {
|
unObserve = openmct.objects.observe(parentObject, '*', (newObject) => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -113,6 +114,10 @@ describe("The Move Action plugin", () => {
|
|||||||
moveAction.invoke([childObject, parentObject]);
|
moveAction.invoke([childObject, parentObject]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
unObserve();
|
||||||
|
});
|
||||||
|
|
||||||
it("the child object's identifier should be in the new parent's composition", () => {
|
it("the child object's identifier should be in the new parent's composition", () => {
|
||||||
let newParentChild = anotherParentObject.composition[0];
|
let newParentChild = anotherParentObject.composition[0];
|
||||||
expect(newParentChild).toEqual(childObject.identifier);
|
expect(newParentChild).toEqual(childObject.identifier);
|
||||||
|
Reference in New Issue
Block a user