mirror of
https://github.com/nasa/openmct.git
synced 2025-01-29 15:43:52 +00:00
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>
This commit is contained in:
parent
026eb86f5f
commit
cb8e09c9f9
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
80
src/api/EditorSpec.js
Normal file
80
src/api/EditorSpec.js
Normal file
@ -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();
|
||||
});
|
||||
});
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -90,6 +90,10 @@ class CouchSearchProvider {
|
||||
}
|
||||
|
||||
searchForTags(tagsArray, abortSignal) {
|
||||
if (!tagsArray || !tagsArray.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const filter = {
|
||||
"selector": {
|
||||
"$and": [
|
||||
|
@ -442,7 +442,8 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
removeSeries(plotSeries) {
|
||||
removeSeries(plotSeries, index) {
|
||||
this.seriesModels.splice(index, 1);
|
||||
this.checkSameRangeValue();
|
||||
this.stopListening(plotSeries);
|
||||
},
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user