Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nasa/openmct.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShefali Joshi <simplyrender@gmail.com>2022-10-08 19:04:38 +0300
committerGitHub <noreply@github.com>2022-10-08 19:04:38 +0300
commitcb8e09c9f9ec176884c8b8d193a9f8f01667077e (patch)
tree9ff34b7848ad1addb51f7ff5a1ad2ab97db5d491
parent026eb86f5fab06bd8a36123ba4465dac365ab8f1 (diff)
Master 2.1.1 (#5858)
* Update version * Don't delete annotations if there aren't any (#5829) * don't delete annotations if there aren't any * add test and align playwright-test * align core with test * added annotation describing test * Add `aria-label` for time conductor history button (#5830) * [Overlay Plot] Inspector series and legend sync fix (#5835) * fixed overlay plots to react to series removals correctly, added alias visual to elements pool aliased items * Keep transaction open on failed editor save (#5840) * do not end a transaction on a failed editor save * add unit tests for successful editor save and unsuccessful editor save * If no matching tags, do not attempt tag search (#5839) * do not attempt search if no matching tags * fix timing on test * commit again in hopes that github will run checks * add back null tag check * add some better documentation to tests Co-authored-by: Andrew Henry <akhenry@gmail.com> * Update version for master Co-authored-by: Scott Bell <scott@traclabs.com> Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com> Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov> Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com> Co-authored-by: Andrew Henry <akhenry@gmail.com>
-rw-r--r--e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js41
-rw-r--r--e2e/tests/functional/plugins/notebook/tags.e2e.spec.js25
-rw-r--r--package.json4
-rw-r--r--src/api/Editor.js3
-rw-r--r--src/api/EditorSpec.js80
-rw-r--r--src/api/annotation/AnnotationAPI.js4
-rw-r--r--src/api/annotation/AnnotationAPISpec.js5
-rw-r--r--src/plugins/notebook/components/Notebook.vue4
-rw-r--r--src/plugins/persistence/couch/CouchSearchProvider.js4
-rw-r--r--src/plugins/plot/MctPlot.vue3
-rw-r--r--src/plugins/plot/configuration/Collection.js2
-rw-r--r--src/plugins/timeConductor/ConductorHistory.vue1
-rw-r--r--src/ui/components/tags/TagEditor.vue8
-rw-r--r--src/ui/inspector/ElementItem.vue9
-rw-r--r--src/ui/inspector/elements.scss9
-rw-r--r--src/ui/layout/search/GrandSearchSpec.js9
16 files changed, 195 insertions, 16 deletions
diff --git a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js
index 4edb1ff28..97f5ab3c6 100644
--- a/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js
+++ b/e2e/tests/functional/plugins/notebook/notebookWithCouchDB.e2e.spec.js
@@ -27,7 +27,7 @@ This test suite is dedicated to tests which verify the basic operations surround
const { test, expect } = require('../../../../baseFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
-test.describe('Notebook Network Request Inspection @couchdb', () => {
+test.describe('Notebook Tests with CouchDB @couchdb', () => {
let testNotebook;
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
@@ -221,6 +221,45 @@ test.describe('Notebook Network Request Inspection @couchdb', () => {
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(4);
});
+
+ test('Search tests', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/akhenry/openmct-yamcs/issues/69'
+ });
+ await page.locator('text=To start a new entry, click here or drag and drop any object').click();
+ await page.locator('[aria-label="Notebook Entry Input"]').click();
+ await page.locator('[aria-label="Notebook Entry Input"]').fill(`First Entry`);
+ await page.locator('[aria-label="Notebook Entry Input"]').press('Enter');
+
+ // Add three tags
+ await page.hover(`button:has-text("Add Tag")`);
+ await page.locator(`button:has-text("Add Tag")`).click();
+ await page.locator('[placeholder="Type to select tag"]').click();
+ await page.locator('[aria-label="Autocomplete Options"] >> text=Science').click();
+ await page.waitForSelector('[aria-label="Tag"]:has-text("Science")');
+
+ await page.hover(`button:has-text("Add Tag")`);
+ await page.locator(`button:has-text("Add Tag")`).click();
+ await page.locator('[placeholder="Type to select tag"]').click();
+ await page.locator('[aria-label="Autocomplete Options"] >> text=Drilling').click();
+ await page.waitForSelector('[aria-label="Tag"]:has-text("Drilling")');
+
+ await page.hover(`button:has-text("Add Tag")`);
+ await page.locator(`button:has-text("Add Tag")`).click();
+ await page.locator('[placeholder="Type to select tag"]').click();
+ await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
+ await page.waitForSelector('[aria-label="Tag"]:has-text("Driving")');
+
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
+ await expect(page.locator('[aria-label="Search Result"]').first()).toContainText("Science");
+ await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText("Driving");
+
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
+ await expect(page.locator('text=No results found')).toBeVisible();
+ });
});
// Try to reduce indeterminism of browser requests by only returning fetch requests.
diff --git a/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js b/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js
index e2cda186a..792117b3e 100644
--- a/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js
+++ b/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js
@@ -39,7 +39,7 @@ async function createNotebookAndEntry(page, iterations = 1) {
createDomainObjectWithDefaults(page, { type: 'Notebook' });
for (let iteration = 0; iteration < iterations; iteration++) {
- // Click text=To start a new entry, click here or drag and drop any object
+ // Create an entry
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = ${iteration}`;
await page.locator(entryLocator).click();
@@ -116,7 +116,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
- await expect(page.locator('[aria-label="Search Result"]')).toBeHidden();
+ await expect(page.locator('text=No results found')).toBeVisible();
});
test('Can delete tags', async ({ page }) => {
@@ -133,6 +133,27 @@ test.describe('Tagging in Notebooks @addInit', () => {
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
});
+ test('Can delete entries without tags', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5823'
+ });
+
+ await createNotebookEntryAndTags(page);
+
+ await page.locator('text=To start a new entry, click here or drag and drop any object').click();
+ const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = 1`;
+ await page.locator(entryLocator).click();
+ await page.locator(entryLocator).fill(`An entry without tags`);
+ await page.locator('[aria-label="Notebook Entry Input"] >> nth=1').press('Enter');
+
+ await page.hover('[aria-label="Notebook Entry Input"] >> nth=1');
+ await page.locator('button[title="Delete this entry"]').last().click();
+ await expect(page.locator('text=This action will permanently delete this entry. Do you wish to continue?')).toBeVisible();
+ await page.locator('button:has-text("Ok")').click();
+ await expect(page.locator('text=This action will permanently delete this entry. Do you wish to continue?')).toBeHidden();
+ });
+
test('Can delete objects with tags and neither return in search', async ({ page }) => {
await createNotebookEntryAndTags(page);
// Delete Notebook
diff --git a/package.json b/package.json
index 1293f0ef0..8ea240c62 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "openmct",
- "version": "2.1.1-SNAPSHOT",
+ "version": "2.1.2",
"description": "The Open MCT core platform",
"devDependencies": {
"@babel/eslint-parser": "7.18.9",
@@ -51,7 +51,7 @@
"moment-timezone": "0.5.37",
"nyc": "15.1.0",
"painterro": "1.2.78",
- "playwright-core": "1.26.1",
+ "playwright-core": "1.25.2",
"plotly.js-basic-dist": "2.14.0",
"plotly.js-gl2d-dist": "2.14.0",
"printj": "1.3.1",
diff --git a/src/api/Editor.js b/src/api/Editor.js
index d919da383..b9f00357b 100644
--- a/src/api/Editor.js
+++ b/src/api/Editor.js
@@ -63,10 +63,9 @@ export default class Editor extends EventEmitter {
.then(() => {
this.editing = false;
this.emit('isEditing', false);
+ this.openmct.objects.endTransaction();
}).catch(error => {
throw error;
- }).finally(() => {
- this.openmct.objects.endTransaction();
});
}
diff --git a/src/api/EditorSpec.js b/src/api/EditorSpec.js
new file mode 100644
index 000000000..104a4f6eb
--- /dev/null
+++ b/src/api/EditorSpec.js
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * 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 Editor API', () => {
+ let openmct;
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+ openmct.on('start', done);
+
+ spyOn(openmct.objects, 'endTransaction');
+
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('opens a transaction on edit', () => {
+ expect(
+ openmct.objects.isTransactionActive()
+ ).toBeFalse();
+ openmct.editor.edit();
+ expect(
+ openmct.objects.isTransactionActive()
+ ).toBeTrue();
+ });
+
+ it('closes an open transaction on successful save', async () => {
+ spyOn(openmct.objects, 'getActiveTransaction')
+ .and.returnValue({
+ commit: () => Promise.resolve(true)
+ });
+
+ openmct.editor.edit();
+ await openmct.editor.save();
+
+ expect(
+ openmct.objects.endTransaction
+ ).toHaveBeenCalled();
+ });
+
+ it('does not close an open transaction on failed save', async () => {
+ spyOn(openmct.objects, 'getActiveTransaction')
+ .and.returnValue({
+ commit: () => Promise.reject()
+ });
+
+ openmct.editor.edit();
+ await openmct.editor.save().catch(() => {});
+
+ expect(
+ openmct.objects.endTransaction
+ ).not.toHaveBeenCalled();
+ });
+});
diff --git a/src/api/annotation/AnnotationAPI.js b/src/api/annotation/AnnotationAPI.js
index 148b1d3c4..618d6f874 100644
--- a/src/api/annotation/AnnotationAPI.js
+++ b/src/api/annotation/AnnotationAPI.js
@@ -346,6 +346,10 @@ export default class AnnotationAPI extends EventEmitter {
*/
async searchForTags(query, abortController) {
const matchingTagKeys = this.#getMatchingTags(query);
+ if (!matchingTagKeys.length) {
+ return [];
+ }
+
const searchResults = (await Promise.all(this.openmct.objects.search(matchingTagKeys, abortController, this.openmct.objects.SEARCH_TYPES.TAGS))).flat();
const filteredDeletedResults = searchResults.filter((result) => {
return !(result._deleted);
diff --git a/src/api/annotation/AnnotationAPISpec.js b/src/api/annotation/AnnotationAPISpec.js
index a7e2a162d..cc362d8a1 100644
--- a/src/api/annotation/AnnotationAPISpec.js
+++ b/src/api/annotation/AnnotationAPISpec.js
@@ -185,5 +185,10 @@ describe("The Annotation API", () => {
expect(results).toBeDefined();
expect(results.length).toEqual(1);
});
+ it("returns no tags for empty search", async () => {
+ const results = await openmct.annotation.searchForTags('q');
+ expect(results).toBeDefined();
+ expect(results.length).toEqual(0);
+ });
});
});
diff --git a/src/plugins/notebook/components/Notebook.vue b/src/plugins/notebook/components/Notebook.vue
index 327205a92..14a5cc48f 100644
--- a/src/plugins/notebook/components/Notebook.vue
+++ b/src/plugins/notebook/components/Notebook.vue
@@ -515,7 +515,9 @@ export default {
});
},
removeAnnotations(entryId) {
- this.openmct.annotation.deleteAnnotations(this.notebookAnnotations[entryId]);
+ if (this.notebookAnnotations[entryId]) {
+ this.openmct.annotation.deleteAnnotations(this.notebookAnnotations[entryId]);
+ }
},
checkEntryPos(entry) {
const entryPos = getEntryPosById(entry.id, this.domainObject, this.selectedSection, this.selectedPage);
diff --git a/src/plugins/persistence/couch/CouchSearchProvider.js b/src/plugins/persistence/couch/CouchSearchProvider.js
index 5d9ef5e4f..d89d13050 100644
--- a/src/plugins/persistence/couch/CouchSearchProvider.js
+++ b/src/plugins/persistence/couch/CouchSearchProvider.js
@@ -90,6 +90,10 @@ class CouchSearchProvider {
}
searchForTags(tagsArray, abortSignal) {
+ if (!tagsArray || !tagsArray.length) {
+ return [];
+ }
+
const filter = {
"selector": {
"$and": [
diff --git a/src/plugins/plot/MctPlot.vue b/src/plugins/plot/MctPlot.vue
index 7db95295c..125ab557e 100644
--- a/src/plugins/plot/MctPlot.vue
+++ b/src/plugins/plot/MctPlot.vue
@@ -442,7 +442,8 @@ export default {
});
},
- removeSeries(plotSeries) {
+ removeSeries(plotSeries, index) {
+ this.seriesModels.splice(index, 1);
this.checkSameRangeValue();
this.stopListening(plotSeries);
},
diff --git a/src/plugins/plot/configuration/Collection.js b/src/plugins/plot/configuration/Collection.js
index 57101ff2d..f35a96ff6 100644
--- a/src/plugins/plot/configuration/Collection.js
+++ b/src/plugins/plot/configuration/Collection.js
@@ -102,8 +102,8 @@ export default class Collection extends Model {
throw new Error('model not found in collection.');
}
- this.emit('remove', model, index);
this.models.splice(index, 1);
+ this.emit('remove', model, index);
}
destroy(model) {
diff --git a/src/plugins/timeConductor/ConductorHistory.vue b/src/plugins/timeConductor/ConductorHistory.vue
index 8fe3b980e..87087c908 100644
--- a/src/plugins/timeConductor/ConductorHistory.vue
+++ b/src/plugins/timeConductor/ConductorHistory.vue
@@ -26,6 +26,7 @@
>
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button
+ aria-label="Time Conductor History"
class="c-button--menu c-history-button icon-history"
@click.prevent.stop="showHistoryMenu"
>
diff --git a/src/ui/components/tags/TagEditor.vue b/src/ui/components/tags/TagEditor.vue
index e1353d834..3c4f137be 100644
--- a/src/ui/components/tags/TagEditor.vue
+++ b/src/ui/components/tags/TagEditor.vue
@@ -145,10 +145,10 @@ export default {
const annotationsToDelete = this.annotations.filter((annotation) => {
return annotation.tags.includes(tagToRemove);
});
- const result = await this.openmct.annotation.deleteAnnotations(annotationsToDelete);
- this.$emit('tags-updated', annotationsToDelete);
-
- return result;
+ if (annotationsToDelete) {
+ await this.openmct.annotation.deleteAnnotations(annotationsToDelete);
+ this.$emit('tags-updated', annotationsToDelete);
+ }
},
async tagAdded(newTag) {
// Either undelete an annotation, or create one (1) new annotation
diff --git a/src/ui/inspector/ElementItem.vue b/src/ui/inspector/ElementItem.vue
index 9d7be7085..0235241b1 100644
--- a/src/ui/inspector/ElementItem.vue
+++ b/src/ui/inspector/ElementItem.vue
@@ -33,7 +33,8 @@
class="c-tree__item c-elements-pool__item"
:class="{
'is-context-clicked': contextClickActive,
- 'hover': hover
+ 'hover': hover,
+ 'is-alias': isAlias
}"
>
<span
@@ -55,6 +56,7 @@ export default {
components: {
ObjectLabel
},
+ inject: ['openmct'],
props: {
index: {
type: Number,
@@ -82,9 +84,12 @@ export default {
}
},
data() {
+ const isAlias = this.elementObject.location !== this.openmct.objects.makeKeyString(this.parentObject.identifier);
+
return {
contextClickActive: false,
- hover: false
+ hover: false,
+ isAlias
};
},
methods: {
diff --git a/src/ui/inspector/elements.scss b/src/ui/inspector/elements.scss
index 14d90b570..e1c519901 100644
--- a/src/ui/inspector/elements.scss
+++ b/src/ui/inspector/elements.scss
@@ -8,6 +8,15 @@
margin-top: $interiorMargin;
}
+ &__item {
+ &.is-alias {
+ // Object is an alias to an original.
+ [class*='__type-icon'] {
+ @include isAlias();
+ }
+ }
+ }
+
&__search {
flex: 0 0 auto;
}
diff --git a/src/ui/layout/search/GrandSearchSpec.js b/src/ui/layout/search/GrandSearchSpec.js
index 5235fc2b3..a4b84515a 100644
--- a/src/ui/layout/search/GrandSearchSpec.js
+++ b/src/ui/layout/search/GrandSearchSpec.js
@@ -232,6 +232,8 @@ describe("GrandSearch", () => {
it("should render an object search result if new object added", async () => {
const composition = openmct.composition.get(mockFolderObject);
composition.add(mockNewObject);
+ // after adding, need to wait a beat for the folder to be indexed
+ await Vue.nextTick();
await grandSearchComponent.$children[0].searchEverything('apple');
await Vue.nextTick();
const searchResults = document.querySelectorAll('[aria-label="New Apple Test Folder folder result"]');
@@ -271,6 +273,13 @@ describe("GrandSearch", () => {
expect(annotationResults[1].innerText).toContain('Driving');
});
+ it("should render no annotation search results if no match", async () => {
+ await grandSearchComponent.$children[0].searchEverything('Qbert');
+ await Vue.nextTick();
+ const annotationResults = document.querySelectorAll('[aria-label="Search Result"]');
+ expect(annotationResults.length).toBe(0);
+ });
+
it("should preview object search results in edit mode if object clicked", async () => {
await grandSearchComponent.$children[0].searchEverything('Folder');
grandSearchComponent._provided.openmct.router.path = [mockDisplayLayout];