mirror of
https://github.com/nasa/openmct.git
synced 2025-02-18 16:40:58 +00:00
Tree item abort (#6757)
* adding abortSignal back to composition load * suppress AbortError console.errors from couch, delay requests for test to trigger abort --------- Co-authored-by: John Hill <john.c.hill@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
cde8fbbb0d
commit
92329b3d8e
@ -192,8 +192,12 @@ test.describe('Persistence operations @couchdb', () => {
|
||||
]);
|
||||
|
||||
//Slow down the test a bit
|
||||
await expect(page.getByRole('treeitem', { name: ` ${myItemsFolderName}` })).toBeVisible();
|
||||
await expect(page2.getByRole('treeitem', { name: ` ${myItemsFolderName}` })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: `Expand ${myItemsFolderName} folder` })
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page2.getByRole('button', { name: `Expand ${myItemsFolderName} folder` })
|
||||
).toBeVisible();
|
||||
|
||||
// Both pages: Click the Create button
|
||||
await Promise.all([
|
||||
|
@ -174,6 +174,42 @@ test.describe('Main Tree', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
test('Opening and closing an item before the request has been fulfilled will abort the request @couchdb', async ({
|
||||
page,
|
||||
openmctConfig
|
||||
}) => {
|
||||
const { myItemsFolderName } = openmctConfig;
|
||||
let requestWasAborted = false;
|
||||
|
||||
page.on('requestfailed', (request) => {
|
||||
// check if the request was aborted
|
||||
if (request.failure().errorText === 'net::ERR_ABORTED') {
|
||||
requestWasAborted = true;
|
||||
}
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder',
|
||||
name: 'Foo'
|
||||
});
|
||||
|
||||
// Intercept and delay request
|
||||
const delayInMs = 500;
|
||||
|
||||
await page.route('**', async (route, request) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, delayInMs));
|
||||
route.continue();
|
||||
});
|
||||
|
||||
// Quickly Expand/close the root folder
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: `Expand ${myItemsFolderName} folder`
|
||||
})
|
||||
.dblclick({ delay: 400 });
|
||||
|
||||
expect(requestWasAborted).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -242,11 +242,16 @@ export default class ObjectAPI {
|
||||
return domainObject;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn(`Failed to retrieve ${keystring}:`, error);
|
||||
delete this.cache[keystring];
|
||||
const result = this.applyGetInterceptors(identifier);
|
||||
|
||||
return result;
|
||||
// suppress abort errors
|
||||
if (error.name === 'AbortError') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(`Failed to retrieve ${keystring}:`, error);
|
||||
|
||||
return this.applyGetInterceptors(identifier);
|
||||
});
|
||||
|
||||
this.cache[keystring] = objectPromise;
|
||||
|
@ -248,10 +248,17 @@ describe('The Object API', () => {
|
||||
});
|
||||
|
||||
it('displays a notification in the event of an error', () => {
|
||||
mockProvider.get.and.returnValue(Promise.reject());
|
||||
openmct.notifications.warn = jasmine.createSpy('warn');
|
||||
mockProvider.get.and.returnValue(
|
||||
Promise.reject({
|
||||
name: 'Error',
|
||||
status: 404,
|
||||
statusText: 'Not Found'
|
||||
})
|
||||
);
|
||||
|
||||
return objectAPI.get(mockDomainObject.identifier).catch(() => {
|
||||
expect(openmct.notifications.error).toHaveBeenCalledWith(
|
||||
expect(openmct.notifications.warn).toHaveBeenCalledWith(
|
||||
`Failed to retrieve object ${TEST_NAMESPACE}:${TEST_KEY}`
|
||||
);
|
||||
});
|
||||
|
@ -223,10 +223,16 @@ class CouchObjectProvider {
|
||||
|
||||
return json;
|
||||
} catch (error) {
|
||||
// abort errors are expected
|
||||
if (error.name === 'AbortError') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Network error, CouchDB unreachable.
|
||||
if (response === null) {
|
||||
this.indicator.setIndicatorToState(DISCONNECTED);
|
||||
console.error(error.message);
|
||||
|
||||
throw new Error(`CouchDB Error - No response"`);
|
||||
} else {
|
||||
if (body?.model && isNotebookOrAnnotationType(body.model)) {
|
||||
|
@ -22,7 +22,12 @@
|
||||
<template>
|
||||
<span
|
||||
:class="[controlClass, { 'c-disclosure-triangle--expanded': value }, { 'is-enabled': enabled }]"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:aria-label="ariaLabelValue"
|
||||
:aria-expanded="value ? 'true' : 'false'"
|
||||
@click="handleClick"
|
||||
@keydown.enter="handleClick"
|
||||
></span>
|
||||
</template>
|
||||
|
||||
@ -42,6 +47,18 @@ export default {
|
||||
controlClass: {
|
||||
type: String,
|
||||
default: 'c-disclosure-triangle'
|
||||
},
|
||||
domainObject: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ariaLabelValue() {
|
||||
const name = this.domainObject.name ? ` ${this.domainObject.name}` : '';
|
||||
const type = this.domainObject.type ? ` ${this.domainObject.type}` : '';
|
||||
|
||||
return `${this.value ? 'Collapse' : 'Expand'}${name}${type}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -326,12 +326,13 @@ export default {
|
||||
},
|
||||
async openTreeItem(parentItem) {
|
||||
const parentPath = parentItem.navigationPath;
|
||||
const abortSignal = this.startItemLoad(parentPath);
|
||||
|
||||
this.startItemLoad(parentPath);
|
||||
// pass in abort signal when functional
|
||||
const childrenItems = await this.loadAndBuildTreeItemsFor(
|
||||
parentItem.object.identifier,
|
||||
parentItem.objectPath
|
||||
parentItem.objectPath,
|
||||
abortSignal
|
||||
);
|
||||
const parentIndex = this.treeItems.indexOf(parentItem);
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
<view-control
|
||||
ref="action"
|
||||
class="c-tree__item__view-control"
|
||||
:domain-object="node.object"
|
||||
:value="isOpen || isLoading"
|
||||
:enabled="!activeSearch && hasComposition"
|
||||
@input="itemAction()"
|
||||
|
Loading…
x
Reference in New Issue
Block a user