mirror of
https://github.com/nasa/openmct.git
synced 2025-04-24 21:10:05 +00:00
Automatically check additional views for memory leaks on navigation (#7300)
* add new objects for navigation testing * add test for remaining objects * cleanup plotly on dismount * lint * remove vestigial object * do not need to call destroy here * do not need to call destroy here * refactor: ensure path to test file always resolves * refactor: better locators --------- Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
This commit is contained in:
parent
2675220452
commit
3fccac0bfc
File diff suppressed because one or more lines are too long
@ -19,10 +19,15 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/* global __dirname */
|
||||||
|
|
||||||
const { test, expect } = require('@playwright/test');
|
const { test, expect } = require('@playwright/test');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const memoryLeakFilePath = 'e2e/test-data/memory-leak-detection.json';
|
const memoryLeakFilePath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../../../e2e/test-data/memory-leak-detection.json'
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* Executes tests to verify that views are not leaking memory on navigation away. This sort of
|
* Executes tests to verify that views are not leaking memory on navigation away. This sort of
|
||||||
* memory leak is generally caused by a failure to clean up registered listeners.
|
* memory leak is generally caused by a failure to clean up registered listeners.
|
||||||
@ -40,54 +45,127 @@ const memoryLeakFilePath = 'e2e/test-data/memory-leak-detection.json';
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NAV_LEAK_TIMEOUT = 10 * 1000; // 10s
|
|
||||||
test.describe('Navigation memory leak is not detected in', () => {
|
test.describe('Navigation memory leak is not detected in', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
await page.locator('a:has-text("My Items")').click({
|
await page
|
||||||
|
.getByRole('treeitem', {
|
||||||
|
name: /My Items/
|
||||||
|
})
|
||||||
|
.click({
|
||||||
button: 'right'
|
button: 'right'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.locator('text=Import from JSON').click();
|
await page
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: /Import from JSON/
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
// Upload memory-leak-detection.json
|
// Upload memory-leak-detection.json
|
||||||
await page.setInputFiles('#fileElem', memoryLeakFilePath);
|
await page.setInputFiles('#fileElem', memoryLeakFilePath);
|
||||||
|
|
||||||
await page.locator('text=OK').click();
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'Save'
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
await expect(page.locator('a:has-text("Memory Leak Detection")')).toBeVisible();
|
await expect(page.locator('a:has-text("Memory Leak Detection")')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('plot view', async ({ page }) => {
|
test('gauge', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(page, 'overlay-plot-single-1hz-swg', {
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'gauge-single-1hz-swg');
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('plan', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'plan-generated');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('time list', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'time-list');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scatter', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'scatter-plot-single-1hz-swg');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('graph', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'graph-single-1hz-swg');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('gantt chart', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'gantt-chart');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clock', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'clock');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('timer', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'timer-far-future');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('web page (nasa.gov)', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'web-page');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Complex Display Layout', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'complex-display-layout');
|
||||||
|
|
||||||
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('plot view', async ({ page }) => {
|
||||||
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'overlay-plot-single-1hz-swg');
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('stacked plot view', async ({ page }) => {
|
test('stacked plot view', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(page, 'stacked-plot-single-1hz-swg', {
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'stacked-plot-single-1hz-swg');
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
});
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('LAD table view', async ({ page }) => {
|
test('LAD table view', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(page, 'lad-table-single-1hz-swg', {
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'lad-table-single-1hz-swg');
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
});
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('LAD table set', async ({ page }) => {
|
test('LAD table set', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(page, 'lad-table-set-single-1hz-swg', {
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'lad-table-set-single-1hz-swg');
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
});
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
@ -96,10 +174,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('telemetry table view', async ({ page }) => {
|
test('telemetry table view', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'telemetry-table-single-1hz-swg',
|
'telemetry-table-single-1hz-swg'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -110,10 +185,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('notebook view', async ({ page }) => {
|
test('notebook view', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'notebook-memory-leak-detection-test',
|
'notebook-memory-leak-detection-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -121,13 +193,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('display layout of a single SWG alphanumeric', async ({ page }) => {
|
test('display layout of a single SWG alphanumeric', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(page, 'display-layout-single-1hz-swg');
|
||||||
page,
|
|
||||||
'display-layout-single-1hz-swg',
|
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
@ -136,10 +202,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('display layout of a single SWG plot', async ({ page }) => {
|
test('display layout of a single SWG plot', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'display-layout-single-overlay-plot',
|
'display-layout-single-overlay-plot'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -150,10 +213,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('example imagery view', async ({ page }) => {
|
test('example imagery view', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'example-imagery-memory-leak-test',
|
'example-imagery-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 // 1 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -163,10 +223,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('display layout of example imagery views', async ({ page }) => {
|
test('display layout of example imagery views', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'display-layout-images-memory-leak-test',
|
'display-layout-images-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 // 1 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -178,10 +235,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
}) => {
|
}) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'display-layout-simple-telemetry',
|
'display-layout-simple-telemetry'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 // 1 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -191,10 +245,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('flexible layout with plots of swgs', async ({ page }) => {
|
test('flexible layout with plots of swgs', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'flexible-layout-plots-memory-leak-test',
|
'flexible-layout-plots-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -204,10 +255,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('flexible layout of example imagery views', async ({ page }) => {
|
test('flexible layout of example imagery views', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'flexible-layout-images-memory-leak-test',
|
'flexible-layout-images-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 // 1 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -217,10 +265,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('tabbed view of display layouts and time strips', async ({ page }) => {
|
test('tabbed view of display layouts and time strips', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'tab-view-simple-memory-leak-test',
|
'tab-view-simple-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 * 2 // 2 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -230,10 +275,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
test('time strip view of telemetry', async ({ page }) => {
|
test('time strip view of telemetry', async ({ page }) => {
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'time-strip-telemetry-memory-leak-test',
|
'time-strip-telemetry-memory-leak-test'
|
||||||
{
|
|
||||||
timeout: NAV_LEAK_TIMEOUT * 6 // 1 min
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
// If we got here without timing out, then the root view object was garbage collected and no memory leak was detected.
|
||||||
@ -247,15 +289,12 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async function navigateToObjectAndDetectMemoryLeak(page, objectName) {
|
async function navigateToObjectAndDetectMemoryLeak(page, objectName) {
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||||
// Fill Search input
|
// Fill Search input
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill(objectName);
|
await page.getByRole('searchbox', { name: 'Search Input' }).fill(objectName);
|
||||||
|
|
||||||
//Search Result Appears and is clicked
|
//Search Result Appears and is clicked
|
||||||
await Promise.all([
|
await page.getByText(objectName, { exact: true }).click();
|
||||||
page.locator(`div.c-gsearch-result__title:has-text("${objectName}")`).first().click(),
|
|
||||||
page.waitForNavigation()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Register a finalization listener on the root node for the view. This tends to be the last thing to be
|
// Register a finalization listener on the root node for the view. This tends to be the last thing to be
|
||||||
// garbage collected since it has either direct or indirect references to all resources used by the view. Therefore it's a pretty good proxy
|
// garbage collected since it has either direct or indirect references to all resources used by the view. Therefore it's a pretty good proxy
|
||||||
@ -273,8 +312,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Nav back to folder
|
// Nav back to folder
|
||||||
await page.goto('./#/browse/mine', { waitUntil: 'networkidle' });
|
await page.goto('./#/browse/mine');
|
||||||
await page.waitForNavigation();
|
|
||||||
|
|
||||||
// This next code block blocks until the finalization listener is called and the gcPromise resolved. This means that the root node for the view has been garbage collected.
|
// This next code block blocks until the finalization listener is called and the gcPromise resolved. This means that the root node for the view has been garbage collected.
|
||||||
// In the event that the root node is not garbage collected, the gcPromise will never resolve and the test will time out.
|
// In the event that the root node is not garbage collected, the gcPromise will never resolve and the test will time out.
|
||||||
|
@ -99,6 +99,7 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
if (this.plotResizeObserver) {
|
if (this.plotResizeObserver) {
|
||||||
|
this.plotResizeObserver.unobserve(this.$refs.plotWrapper);
|
||||||
this.plotResizeObserver.disconnect();
|
this.plotResizeObserver.disconnect();
|
||||||
clearTimeout(this.resizeTimer);
|
clearTimeout(this.resizeTimer);
|
||||||
}
|
}
|
||||||
@ -106,6 +107,8 @@ export default {
|
|||||||
if (this.removeBarColorListener) {
|
if (this.removeBarColorListener) {
|
||||||
this.removeBarColorListener();
|
this.removeBarColorListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plotly.purge(this.$refs.plot);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getAxisMinMax(axis) {
|
getAxisMinMax(axis) {
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<series-options
|
<series-options
|
||||||
v-for="series in plotSeries"
|
v-for="series in plotSeries"
|
||||||
:key="series.key"
|
:key="series.keyString"
|
||||||
:item="series"
|
:item="series"
|
||||||
:color-palette="colorPalette"
|
:color-palette="colorPalette"
|
||||||
/>
|
/>
|
||||||
|
@ -217,7 +217,6 @@ describe('the plugin', function () {
|
|||||||
'someNamespace:~OpenMCT~outer.test-object.foo.bar'
|
'someNamespace:~OpenMCT~outer.test-object.foo.bar'
|
||||||
].name
|
].name
|
||||||
).toEqual('A Dotful Object');
|
).toEqual('A Dotful Object');
|
||||||
barGraphView.destroy();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -310,7 +309,6 @@ describe('the plugin', function () {
|
|||||||
|
|
||||||
const plotElement = element.querySelector('.cartesianlayer .scatterlayer .trace .lines');
|
const plotElement = element.querySelector('.cartesianlayer .scatterlayer .trace .lines');
|
||||||
expect(plotElement).not.toBeNull();
|
expect(plotElement).not.toBeNull();
|
||||||
barGraphView.destroy();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -131,6 +131,8 @@ export default {
|
|||||||
if (this.unobserveColorChanges) {
|
if (this.unobserveColorChanges) {
|
||||||
this.unobserveColorChanges();
|
this.unobserveColorChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plotly.purge(this.$refs.plot);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getUnderlayPlotData() {
|
getUnderlayPlotData() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user