mirror of
https://github.com/nasa/openmct.git
synced 2025-03-22 03:55:31 +00:00
5361 tags not persisting locally (#5408)
* fixed typo * remove unneeded lookup * fix tags adding and deleting * more reliable way to remove tags * break tests up for parallel execution * fixed notebook tagging test * enable e2e tests * made schedule index comment more clear and fix uppercase/lowercase issue * address e2e changes * add unit test to bump coverage * fix typo * need to check on annotation creation if provider exists or not * added fixtures * undo silly couchdb commit
This commit is contained in:
parent
28dbd724d6
commit
2bd35bb2a5
146
e2e/tests/plugins/notebook/tags.e2e.spec.js
Normal file
146
e2e/tests/plugins/notebook/tags.e2e.spec.js
Normal file
@ -0,0 +1,146 @@
|
||||
/*****************************************************************************
|
||||
* 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 { expect } = require('@playwright/test');
|
||||
const { test } = require('../../../fixtures');
|
||||
|
||||
/**
|
||||
* Creates a notebook object and adds an entry.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createNotebookAndEntry(page) {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Click button:has-text("Create")
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
// Click li:has-text("Notebook")
|
||||
await page.locator('li:has-text("Notebook")').click();
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('button:has-text("OK")').click()
|
||||
]);
|
||||
|
||||
// Click text=To start a new entry, click here or drag and drop any object
|
||||
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a notebook object, adds an entry, and adds a tag.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createNotebookEntryAndTags(page) {
|
||||
await createNotebookAndEntry(page);
|
||||
// Click text=To start a new entry, click here or drag and drop any object
|
||||
await page.locator('button:has-text("Add Tag")').click();
|
||||
|
||||
// Click [placeholder="Type to select tag"]
|
||||
await page.locator('[placeholder="Type to select tag"]').click();
|
||||
// Click text=Driving
|
||||
await page.locator('text=Driving').click();
|
||||
|
||||
// Click button:has-text("Add Tag")
|
||||
await page.locator('button:has-text("Add Tag")').click();
|
||||
// Click [placeholder="Type to select tag"]
|
||||
await page.locator('[placeholder="Type to select tag"]').click();
|
||||
// Click text=Science
|
||||
await page.locator('text=Science').click();
|
||||
}
|
||||
|
||||
test.describe('Tagging in Notebooks', () => {
|
||||
test('Can load tags', async ({ page }) => {
|
||||
await createNotebookAndEntry(page);
|
||||
// Click text=To start a new entry, click here or drag and drop any object
|
||||
await page.locator('button:has-text("Add Tag")').click();
|
||||
|
||||
// Click [placeholder="Type to select tag"]
|
||||
await page.locator('[placeholder="Type to select tag"]').click();
|
||||
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Driving");
|
||||
});
|
||||
test('Can add tags', async ({ page }) => {
|
||||
await createNotebookEntryAndTags(page);
|
||||
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
|
||||
|
||||
// Click button:has-text("Add Tag")
|
||||
await page.locator('button:has-text("Add Tag")').click();
|
||||
// Click [placeholder="Type to select tag"]
|
||||
await page.locator('[placeholder="Type to select tag"]').click();
|
||||
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Driving");
|
||||
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
|
||||
});
|
||||
test('Can search for tags', async ({ page }) => {
|
||||
await createNotebookEntryAndTags(page);
|
||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Driving");
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Driving");
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Can delete tags', async ({ page }) => {
|
||||
await createNotebookEntryAndTags(page);
|
||||
await page.locator('[aria-label="Notebook Entries"]').click();
|
||||
// Delete Driving
|
||||
await page.locator('text=Science Driving Add Tag >> button').nth(1).click();
|
||||
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).not.toContainText("Driving");
|
||||
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
|
||||
});
|
||||
test('Tags persist across reload', async ({ page }) => {
|
||||
await createNotebookEntryAndTags(page);
|
||||
//Go to baseURL
|
||||
await page.reload();
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
|
||||
});
|
||||
});
|
@ -89,7 +89,7 @@
|
||||
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
||||
"test:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch notebook/tags",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
||||
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome --grep @snapshot --update-snapshots",
|
||||
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js",
|
||||
|
@ -224,7 +224,8 @@ class InMemorySearchProvider {
|
||||
|
||||
/**
|
||||
* Schedule an id to be indexed at a later date. If there are less
|
||||
* pending requests then allowed, will kick off an indexing request.
|
||||
* pending requests than the maximum allowed, this will kick off an indexing request.
|
||||
* This is done only when indexing first begins and we need to index a lot of objects.
|
||||
*
|
||||
* @private
|
||||
* @param {identifier} id to be indexed.
|
||||
@ -258,8 +259,12 @@ class InMemorySearchProvider {
|
||||
}
|
||||
|
||||
onAnnotationCreation(annotationObject) {
|
||||
const provider = this;
|
||||
provider.index(annotationObject);
|
||||
|
||||
const objectProvider = this.openmct.objects.getProvider(annotationObject.identifier);
|
||||
if (objectProvider === undefined || objectProvider.search === undefined) {
|
||||
const provider = this;
|
||||
provider.index(annotationObject);
|
||||
}
|
||||
}
|
||||
|
||||
onNameMutation(domainObject, name) {
|
||||
@ -270,7 +275,6 @@ class InMemorySearchProvider {
|
||||
}
|
||||
|
||||
onTagMutation(domainObject, newTags) {
|
||||
domainObject.oldTags = domainObject.tags;
|
||||
domainObject.tags = newTags;
|
||||
const provider = this;
|
||||
|
||||
@ -404,20 +408,16 @@ class InMemorySearchProvider {
|
||||
}
|
||||
|
||||
});
|
||||
// remove old tags
|
||||
if (model.oldTags) {
|
||||
model.oldTags.forEach(tagIDToRemove => {
|
||||
const existsInNewModel = model.tags.includes(tagIDToRemove);
|
||||
if (!existsInNewModel && this.localIndexedAnnotationsByTag[tagIDToRemove]) {
|
||||
this.localIndexedAnnotationsByTag[tagIDToRemove] = this.localIndexedAnnotationsByTag[tagIDToRemove].
|
||||
filter(annotationToRemove => {
|
||||
const shouldKeep = annotationToRemove.keyString !== keyString;
|
||||
const tagsToRemoveFromIndex = Object.keys(this.localIndexedAnnotationsByTag).filter(indexedTag => {
|
||||
return !(model.tags.includes(indexedTag));
|
||||
});
|
||||
tagsToRemoveFromIndex.forEach(tagToRemoveFromIndex => {
|
||||
this.localIndexedAnnotationsByTag[tagToRemoveFromIndex] = this.localIndexedAnnotationsByTag[tagToRemoveFromIndex].filter(indexedAnnotation => {
|
||||
const shouldKeep = indexedAnnotation.keyString !== keyString;
|
||||
|
||||
return shouldKeep;
|
||||
});
|
||||
}
|
||||
return shouldKeep;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
localIndexAnnotation(objectToIndex, model) {
|
||||
@ -449,7 +449,7 @@ class InMemorySearchProvider {
|
||||
keyString
|
||||
};
|
||||
if (model && (model.type === 'annotation')) {
|
||||
if (model.targets && model.targets) {
|
||||
if (model.targets) {
|
||||
this.localIndexAnnotation(objectToIndex, model);
|
||||
}
|
||||
|
||||
|
@ -94,19 +94,16 @@
|
||||
|
||||
});
|
||||
// remove old tags
|
||||
if (model.oldTags) {
|
||||
model.oldTags.forEach(tagIDToRemove => {
|
||||
const existsInNewModel = model.tags.includes(tagIDToRemove);
|
||||
if (!existsInNewModel && indexedAnnotationsByTag[tagIDToRemove]) {
|
||||
indexedAnnotationsByTag[tagIDToRemove] = indexedAnnotationsByTag[tagIDToRemove].
|
||||
filter(annotationToRemove => {
|
||||
const shouldKeep = annotationToRemove.keyString !== keyString;
|
||||
const tagsToRemoveFromIndex = Object.keys(indexedAnnotationsByTag).filter(indexedTag => {
|
||||
return !(model.tags.includes(indexedTag));
|
||||
});
|
||||
tagsToRemoveFromIndex.forEach(tagToRemoveFromIndex => {
|
||||
indexedAnnotationsByTag[tagToRemoveFromIndex] = indexedAnnotationsByTag[tagToRemoveFromIndex].filter(indexedAnnotation => {
|
||||
const shouldKeep = indexedAnnotation.keyString !== keyString;
|
||||
|
||||
return shouldKeep;
|
||||
});
|
||||
}
|
||||
return shouldKeep;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function indexItem(keyString, model) {
|
||||
@ -116,7 +113,7 @@
|
||||
keyString
|
||||
};
|
||||
if (model && (model.type === 'annotation')) {
|
||||
if (model.targets && model.targets) {
|
||||
if (model.targets) {
|
||||
indexAnnotation(objectToIndex, model);
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,7 @@
|
||||
v-if="selectedSection && selectedPage"
|
||||
ref="notebookEntries"
|
||||
class="c-notebook__entries"
|
||||
aria-label="Notebook Entries"
|
||||
>
|
||||
<NotebookEntry
|
||||
v-for="entry in filteredAndSortedEntries"
|
||||
|
@ -23,6 +23,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-notebook__entry c-ne has-local-controls has-tag-applier"
|
||||
aria-label="Notebook Entry"
|
||||
:class="{ 'locked': isLocked }"
|
||||
@dragover="changeCursor"
|
||||
@drop.capture="cancelEditMode"
|
||||
|
@ -102,7 +102,7 @@ class CouchSearchProvider {
|
||||
},
|
||||
{
|
||||
"model.annotationType": {
|
||||
"$eq": "notebook"
|
||||
"$eq": "NOTEBOOK"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.annontation = await this.openmct.annotation.getAnnotation(this.annotationQuery, this.annotationSearchType);
|
||||
this.annotation = await this.openmct.annotation.getAnnotation(this.annotationQuery, this.annotationSearchType);
|
||||
this.addAnnotationListener(this.annotation);
|
||||
if (this.annotation && this.annotation.tags) {
|
||||
this.tagsChanged(this.annotation.tags);
|
||||
@ -133,21 +133,18 @@ export default {
|
||||
this.addedTags.push(newTagValue);
|
||||
this.userAddingTag = true;
|
||||
},
|
||||
async tagRemoved(tagToRemove) {
|
||||
const existingAnnotation = await this.openmct.annotation.getAnnotation(this.annotationQuery, this.annotationSearchType);
|
||||
|
||||
return this.openmct.annotation.removeAnnotationTag(existingAnnotation, tagToRemove);
|
||||
tagRemoved(tagToRemove) {
|
||||
return this.openmct.annotation.removeAnnotationTag(this.annotation, tagToRemove);
|
||||
},
|
||||
async tagAdded(newTag) {
|
||||
const existingAnnotation = await this.openmct.annotation.getAnnotation(this.annotationQuery, this.annotationSearchType);
|
||||
|
||||
const newAnnotation = await this.openmct.annotation.addAnnotationTag(existingAnnotation,
|
||||
const annotationWasCreated = this.annotation === null || this.annotation === undefined;
|
||||
this.annotation = await this.openmct.annotation.addAnnotationTag(this.annotation,
|
||||
this.domainObject, this.targetSpecificDetails, this.annotationType, newTag);
|
||||
if (!this.annotation) {
|
||||
this.addAnnotationListener(newAnnotation);
|
||||
if (annotationWasCreated) {
|
||||
this.addAnnotationListener(this.annotation);
|
||||
}
|
||||
|
||||
this.tagsChanged(newAnnotation.tags);
|
||||
this.tagsChanged(this.annotation.tags);
|
||||
this.userAddingTag = false;
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,6 @@ export default {
|
||||
objectSearchResults: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
destroyed() {
|
||||
document.body.removeEventListener('click', this.handleOutsideClick);
|
||||
},
|
||||
|
@ -36,9 +36,9 @@ describe("GrandSearch", () => {
|
||||
let parent;
|
||||
let sharedWorkerToRestore;
|
||||
let mockDomainObject;
|
||||
let mockAnnotationObject;
|
||||
let mockDisplayLayout;
|
||||
let mockFolderObject;
|
||||
let mockAnnotationObject;
|
||||
let originalRouterPath;
|
||||
|
||||
beforeEach((done) => {
|
||||
@ -107,7 +107,6 @@ describe("GrandSearch", () => {
|
||||
};
|
||||
|
||||
openmct.router.isNavigatedObject = jasmine.createSpy().and.returnValue(false);
|
||||
|
||||
const mockObjectProvider = jasmine.createSpyObj("mock object provider", [
|
||||
"create",
|
||||
"update",
|
||||
|
Loading…
x
Reference in New Issue
Block a user