Compare commits
	
		
			98 Commits
		
	
	
		
			release/2.
			...
			fix-gauge
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a73719e809 | ||
| 
						 | 
					798e2d4337 | ||
| 
						 | 
					e3770dc701 | ||
| 
						 | 
					fcfa95c2df | ||
| 
						 | 
					0f12aa1eae | ||
| 
						 | 
					d673cc6bcc | ||
| 
						 | 
					c17efcc157 | ||
| 
						 | 
					90662ce4a7 | ||
| 
						 | 
					84c1526f5e | ||
| 
						 | 
					b6d8d794be | ||
| 
						 | 
					d73cb2ef70 | ||
| 
						 | 
					07bdbe5108 | ||
| 
						 | 
					f92bc0122c | ||
| 
						 | 
					291e62687e | ||
| 
						 | 
					313390f1fe | ||
| 
						 | 
					e7804dd25e | ||
| 
						 | 
					ec1de92d4b | ||
| 
						 | 
					efadf9036f | ||
| 
						 | 
					ca928370a4 | ||
| 
						 | 
					6820e0d044 | ||
| 
						 | 
					b8fa89af6e | ||
| 
						 | 
					bbb84c695d | ||
| 
						 | 
					f979e170ee | ||
| 
						 | 
					1d875cb8ca | ||
| 
						 | 
					064a865c9b | ||
| 
						 | 
					a584766618 | ||
| 
						 | 
					a671be726b | ||
| 
						 | 
					115912da31 | ||
| 
						 | 
					10ff4e1781 | ||
| 
						 | 
					f1c85933c3 | ||
| 
						 | 
					01aac89be0 | ||
| 
						 | 
					3c7ecc8561 | ||
| 
						 | 
					dee92d893c | ||
| 
						 | 
					0e707150e0 | ||
| 
						 | 
					2540d96617 | ||
| 
						 | 
					8ca0f13cd9 | ||
| 
						 | 
					8ec7fbb74c | ||
| 
						 | 
					143fb2dcdc | ||
| 
						 | 
					1c8784fec5 | ||
| 
						 | 
					a692245644 | ||
| 
						 | 
					2943d2b6ec | ||
| 
						 | 
					4246a597a9 | ||
| 
						 | 
					0af7965021 | ||
| 
						 | 
					d6bd2793d7 | ||
| 
						 | 
					e9c0909415 | ||
| 
						 | 
					0f0a3dc48f | ||
| 
						 | 
					4c82680b87 | ||
| 
						 | 
					450d3a5575 | ||
| 
						 | 
					4f28e3bdf1 | ||
| 
						 | 
					e6d25b22c1 | ||
| 
						 | 
					acc60f5e4e | ||
| 
						 | 
					9911d9ed6a | ||
| 
						 | 
					c4734b8ad6 | ||
| 
						 | 
					9786ff5de4 | ||
| 
						 | 
					437154a5c0 | ||
| 
						 | 
					2bd38dab9f | ||
| 
						 | 
					063df721ae | ||
| 
						 | 
					a09db30b32 | ||
| 
						 | 
					9d89bdd6d3 | ||
| 
						 | 
					ed9ca2829b | ||
| 
						 | 
					eacbac6aad | ||
| 
						 | 
					69153fe8f0 | ||
| 
						 | 
					51196530fd | ||
| 
						 | 
					fefa46ce7e | ||
| 
						 | 
					82e685d4df | ||
| 
						 | 
					e08ab8ef24 | ||
| 
						 | 
					0060a6e20b | ||
| 
						 | 
					7011877e64 | ||
| 
						 | 
					8890cd9b22 | ||
| 
						 | 
					34ecc08238 | ||
| 
						 | 
					a07c043a29 | ||
| 
						 | 
					2999a5135e | ||
| 
						 | 
					2766452b38 | ||
| 
						 | 
					f3cdf69288 | ||
| 
						 | 
					a040bb30c2 | ||
| 
						 | 
					0a2e0a4e65 | ||
| 
						 | 
					e8df2bd437 | ||
| 
						 | 
					ccd2a8b64c | ||
| 
						 | 
					2bd35bb2a5 | ||
| 
						 | 
					28dbd724d6 | ||
| 
						 | 
					5a1c329c66 | ||
| 
						 | 
					00a5cbd2fd | ||
| 
						 | 
					a2d698d5c1 | ||
| 
						 | 
					5685a5b393 | ||
| 
						 | 
					164f39695e | ||
| 
						 | 
					c384cf67da | ||
| 
						 | 
					417b225505 | ||
| 
						 | 
					e5e93f311c | ||
| 
						 | 
					39e6d9c90c | ||
| 
						 | 
					60d021ef82 | ||
| 
						 | 
					59880955a2 | ||
| 
						 | 
					b51ed7e844 | ||
| 
						 | 
					7bbaec4006 | ||
| 
						 | 
					c0f24b3925 | ||
| 
						 | 
					4e79725897 | ||
| 
						 | 
					0674c9fc33 | ||
| 
						 | 
					de1b877954 | ||
| 
						 | 
					4db2f547d9 | 
							
								
								
									
										5
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -7,12 +7,13 @@ updates:
 | 
			
		||||
      interval: "daily"  
 | 
			
		||||
    open-pull-requests-limit: 10
 | 
			
		||||
    labels:
 | 
			
		||||
      - "pr:e2e"
 | 
			
		||||
      - "type:maintenance"
 | 
			
		||||
      - "dependencies"
 | 
			
		||||
      - "pr:e2e"
 | 
			
		||||
      - "pr:daveit"
 | 
			
		||||
      - "pr:visual"
 | 
			
		||||
      - "pr:platform"
 | 
			
		||||
    ignore:
 | 
			
		||||
      - dependency-name: "@playwright/test" #we source the container instead of the dependency in CI
 | 
			
		||||
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
    directory: "/"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								API.md
									
									
									
									
									
								
							@@ -390,7 +390,7 @@ A telemetry object is a domain object with a telemetry property.  To take an exa
 | 
			
		||||
            {
 | 
			
		||||
                "key": "value",
 | 
			
		||||
                "name": "Value",
 | 
			
		||||
                "units": "kilograms",
 | 
			
		||||
                "unit": "kilograms",
 | 
			
		||||
                "format": "float",
 | 
			
		||||
                "min": 0,
 | 
			
		||||
                "max": 100,
 | 
			
		||||
@@ -425,7 +425,7 @@ attribute      | type   | flags    | notes
 | 
			
		||||
`name`         | string | optional | a human readable label for this field.  If omitted, defaults to `key`.
 | 
			
		||||
`source`       | string | optional | identifies the property of a datum where this value is stored.  If omitted, defaults to `key`.
 | 
			
		||||
`format`       | string | optional | a specific format identifier, mapping to a formatter.  If omitted, uses a default formatter.  For enumerations, use `enum`.  For timestamps, use `utc` if you are using utc dates, otherwise use a key mapping to your custom date format.  
 | 
			
		||||
`units`        | string | optional | the units of this value, e.g. `km`, `seconds`, `parsecs`
 | 
			
		||||
`unit`        | string | optional | the unit of this value, e.g. `km`, `seconds`, `parsecs`
 | 
			
		||||
`min`          | number | optional | the minimum possible value of this measurement.  Will be used by plots, gauges, etc to automatically set a min value.
 | 
			
		||||
`max`          | number | optional | the maximum possible value of this measurement.  Will be used by plots, gauges, etc to automatically set a max value.
 | 
			
		||||
`enumerations` | array  | optional | for objects where `format` is `"enum"`, this array tracks all possible enumerations of the value.  Each entry in this array is an object, with a `value` property that is the numerical value of the enumeration, and a `string` property that is the text value of the enumeration.  ex: `{"value": 0, "string": "OFF"}`.  If you use an enumerations array, `min` and `max` will be set automatically for you.
 | 
			
		||||
@@ -1082,4 +1082,4 @@ View provider Example:
 | 
			
		||||
        return openmct.priority.HIGH;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -23,21 +23,23 @@ If this is your first time ever using the Playwright framework, we recommend goi
 | 
			
		||||
Once you've got an understanding of Playwright, you'll need a baseline understanding of Open MCT:
 | 
			
		||||
 | 
			
		||||
1. Follow the steps [Building and Running Open MCT Locally](../README.md#building-and-running-open-mct-locally)
 | 
			
		||||
2. Once you're serving Open MCT locally, create an Example Telemetry Object (e.g.: 'Sine Wave Generator')
 | 
			
		||||
2. Once you're serving Open MCT locally, create a 'Display Layout' object. Save it.
 | 
			
		||||
3. Create a 'Plot' Object (e.g.: 'Stacked Plot')
 | 
			
		||||
4. Expand the Tree on the left-hand nav and drag and drop the Example Telemetry Object into the Plot Object
 | 
			
		||||
5. Create a 'Display Layout' object
 | 
			
		||||
6. From the Tree, Drag the Plot object into the Display Layout
 | 
			
		||||
4. Create an Example Telemetry Object (e.g.: 'Sine Wave Generator')
 | 
			
		||||
5. Expand the Tree and note the hierarchy of objects which were created.
 | 
			
		||||
6. Navigate to the Demo Display Layout Object to edit and modify the embedded plot.
 | 
			
		||||
7. Modify the embedded plot with Telemetry Data.
 | 
			
		||||
 | 
			
		||||
What you've created is a display which mimics the display that a mission control operator might use to understand and model telemetry data.
 | 
			
		||||
 | 
			
		||||
Recreate the steps above with Playwright's codegen tool:
 | 
			
		||||
 | 
			
		||||
1. `npm run start` in a terminal window
 | 
			
		||||
2. Open another terminal window and start the Playwright codegen application `npx playwright codegen`
 | 
			
		||||
3. Navigate the browser to `http://localhost:8080`
 | 
			
		||||
4. Click the Create button and notice how your actions in the browser are being recorded in the Playwright Inspector
 | 
			
		||||
5. Continue through the steps 2-6 above
 | 
			
		||||
1. `npm run start` in a terminal window to serve Open MCT locally
 | 
			
		||||
2. `npx @playwright/test install` to install playwright and dependencies
 | 
			
		||||
3. Open another terminal window and start the Playwright codegen application `npx playwright codegen`
 | 
			
		||||
4. Navigate the browser to `http://localhost:8080`
 | 
			
		||||
5. Click the Create button and notice how your actions in the browser are being recorded in the Playwright Inspector
 | 
			
		||||
6. Continue through the steps 2-6 above
 | 
			
		||||
 | 
			
		||||
What you've created is an automated test which mimics the creation of a mission control display.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,8 @@ const { test, expect } = require('../../../../baseFixtures');
 | 
			
		||||
 | 
			
		||||
test.describe('Sine Wave Generator', () => {
 | 
			
		||||
    test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page, browserName }) => {
 | 
			
		||||
        test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
        // eslint-disable-next-line playwright/no-skipped-test
 | 
			
		||||
        test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
 | 
			
		||||
        //Go to baseURL
 | 
			
		||||
        await page.goto('./', { waitUntil: 'networkidle' });
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ test.describe('Clock Generator CRUD Operations', () => {
 | 
			
		||||
        await page.locator('.icon-arrow-down').click();
 | 
			
		||||
 | 
			
		||||
        // Verify clicking on the autocomplete arrow collapses the dropdown
 | 
			
		||||
        await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
 | 
			
		||||
        await expect(page.locator(".c-input--autocomplete__options")).toBeHidden();
 | 
			
		||||
 | 
			
		||||
        // Click timezone input to open dropdown
 | 
			
		||||
        await page.locator('.c-input--autocomplete__input').click();
 | 
			
		||||
@@ -60,7 +60,7 @@ test.describe('Clock Generator CRUD Operations', () => {
 | 
			
		||||
        // Verify clicking outside the autocomplete dropdown collapses it
 | 
			
		||||
        await page.locator('text=Timezone').click();
 | 
			
		||||
        // Verify clicking on the autocomplete arrow collapses the dropdown
 | 
			
		||||
        await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
 | 
			
		||||
        await expect(page.locator(".c-input--autocomplete__options")).toBeHidden();
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,8 @@ test.describe('Example Imagery Object', () => {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Can adjust image brightness/contrast by dragging the sliders', async ({ page, browserName }) => {
 | 
			
		||||
        test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
        // eslint-disable-next-line playwright/no-skipped-test
 | 
			
		||||
        test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
        // Open the image filter menu
 | 
			
		||||
        await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click();
 | 
			
		||||
 | 
			
		||||
@@ -422,16 +423,12 @@ test.describe('Example imagery thumbnails resize in display layouts', () => {
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// test.fixme('Can use Mouse Wheel to zoom in and out of previous image');
 | 
			
		||||
// test.fixme('Can use alt+drag to move around image once zoomed in');
 | 
			
		||||
// test.fixme('Clicking on the left arrow should pause the imagery and go to previous image');
 | 
			
		||||
// test.fixme('If the imagery view is in pause mode, images still come in');
 | 
			
		||||
// test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in');
 | 
			
		||||
test.describe('Example Imagery in Flexible layout', () => {
 | 
			
		||||
    test('Example Imagery in Flexible layout @unstable', async ({ page, browserName, openmctConfig }) => {
 | 
			
		||||
        const { myItemsFolderName } = openmctConfig;
 | 
			
		||||
 | 
			
		||||
        test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
        // eslint-disable-next-line playwright/no-skipped-test
 | 
			
		||||
        test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
 | 
			
		||||
        test.info().annotations.push({
 | 
			
		||||
            type: 'issue',
 | 
			
		||||
            description: 'https://github.com/nasa/openmct/issues/5326'
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,23 @@ test.describe('Notebook section tests', () => {
 | 
			
		||||
        //Delete 3rd section
 | 
			
		||||
        //1st is selected and there is no default notebook
 | 
			
		||||
    });
 | 
			
		||||
    test.fixme('Section rename operations', async ({ page }) => {
 | 
			
		||||
        // Create a new notebook
 | 
			
		||||
        // Add a section
 | 
			
		||||
        // Rename the section but do not confirm
 | 
			
		||||
        // Keyboard press 'Escape'
 | 
			
		||||
        // Verify that the section name reverts to the default name
 | 
			
		||||
        // Rename the section but do not confirm
 | 
			
		||||
        // Keyboard press 'Enter'
 | 
			
		||||
        // Verify that the section name is updated
 | 
			
		||||
        // Rename the section to "" (empty string)
 | 
			
		||||
        // Keyboard press 'Enter' to confirm
 | 
			
		||||
        // Verify that the section name reverts to the default name
 | 
			
		||||
        // Rename the section to something long that overflows the text box
 | 
			
		||||
        // Verify that the section name is not truncated while input is active
 | 
			
		||||
        // Confirm the section name edit
 | 
			
		||||
        // Verify that the section name is truncated now that input is not active
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Notebook page tests', () => {
 | 
			
		||||
@@ -107,6 +124,23 @@ test.describe('Notebook page tests', () => {
 | 
			
		||||
        //Delete 3rd page
 | 
			
		||||
        //First is now selected and there is no default notebook
 | 
			
		||||
    });
 | 
			
		||||
    test.fixme('Page rename operations', async ({ page }) => {
 | 
			
		||||
        // Create a new notebook
 | 
			
		||||
        // Add a page
 | 
			
		||||
        // Rename the page but do not confirm
 | 
			
		||||
        // Keyboard press 'Escape'
 | 
			
		||||
        // Verify that the page name reverts to the default name
 | 
			
		||||
        // Rename the page but do not confirm
 | 
			
		||||
        // Keyboard press 'Enter'
 | 
			
		||||
        // Verify that the page name is updated
 | 
			
		||||
        // Rename the page to "" (empty string)
 | 
			
		||||
        // Keyboard press 'Enter' to confirm
 | 
			
		||||
        // Verify that the page name reverts to the default name
 | 
			
		||||
        // Rename the page to something long that overflows the text box
 | 
			
		||||
        // Verify that the page name is not truncated while input is active
 | 
			
		||||
        // Confirm the page name edit
 | 
			
		||||
        // Verify that the page name is truncated now that input is not active
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Notebook search tests', () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,8 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Locked page should now be in a locked state @addInit @unstable', async ({ page }, testInfo) => {
 | 
			
		||||
        test.fixme(testInfo.project === 'chrome-beta', "Test is unreliable on chrome-beta");
 | 
			
		||||
        // eslint-disable-next-line playwright/no-skipped-test
 | 
			
		||||
        test.skip(testInfo.project === 'chrome-beta', "Test is unreliable on chrome-beta");
 | 
			
		||||
        // main lock message on page
 | 
			
		||||
        const lockMessage = page.locator('text=This page has been committed and cannot be modified or removed');
 | 
			
		||||
        expect.soft(await lockMessage.count()).toEqual(1);
 | 
			
		||||
 
 | 
			
		||||
@@ -126,8 +126,8 @@ test.describe('Tagging in Notebooks @addInit', () => {
 | 
			
		||||
        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();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).toBeHidden();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).toBeHidden();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Can delete tags', async ({ page }) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,10 @@ const { test, expect } = require('../../../../pluginFixtures');
 | 
			
		||||
 | 
			
		||||
test.describe('Handle missing object for plots', () => {
 | 
			
		||||
    test('Displays empty div for missing stacked plot item @unstable', async ({ page, browserName, openmctConfig }) => {
 | 
			
		||||
        const { myItemsFolderName } = openmctConfig;
 | 
			
		||||
        // eslint-disable-next-line playwright/no-skipped-test
 | 
			
		||||
        test.skip(browserName === 'firefox', 'Firefox failing due to console events being missed');
 | 
			
		||||
 | 
			
		||||
        test.fixme(browserName === 'firefox', 'Firefox failing due to console events being missed');
 | 
			
		||||
        const { myItemsFolderName } = openmctConfig;
 | 
			
		||||
        const errorLogs = [];
 | 
			
		||||
 | 
			
		||||
        page.on("console", (message) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ test.describe('Grand Search', () => {
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(`Clock D ${myItemsFolderName} Red Folder Blue Folder`);
 | 
			
		||||
        // Click text=Elements >> nth=0
 | 
			
		||||
        await page.locator('text=Elements').first().click();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toBeVisible();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
 | 
			
		||||
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
 | 
			
		||||
        await page.locator('[aria-label="Clock A clock result"] >> text=Clock A').click();
 | 
			
		||||
@@ -56,11 +56,11 @@ test.describe('Grand Search', () => {
 | 
			
		||||
 | 
			
		||||
        // Click [aria-label="OpenMCT Search"] a >> nth=0
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] a').first().click();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toBeVisible();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
 | 
			
		||||
 | 
			
		||||
        // Fill [aria-label="OpenMCT Search"] input[type="search"]
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo');
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toBeVisible();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
 | 
			
		||||
 | 
			
		||||
        // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
 | 
			
		||||
        await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 16 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 15 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 18 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										161
									
								
								e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Tests to verify log plot functionality when objects are missing
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
const { test } = require('../../../fixtures.js');
 | 
			
		||||
const { expect } = require('@playwright/test');
 | 
			
		||||
 | 
			
		||||
test.describe('Handle missing object for plots', () => {
 | 
			
		||||
    test('Displays empty div for missing stacked plot item', async ({ page }) => {
 | 
			
		||||
        const errorLogs = [];
 | 
			
		||||
 | 
			
		||||
        page.on("console", (message) => {
 | 
			
		||||
            if (message.type() === 'warning') {
 | 
			
		||||
                errorLogs.push(message.text());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //Make stacked plot
 | 
			
		||||
        await makeStackedPlot(page);
 | 
			
		||||
 | 
			
		||||
        //Gets local storage and deletes the last sine wave generator in the stacked plot
 | 
			
		||||
        const localStorage = await page.evaluate(() => window.localStorage);
 | 
			
		||||
        const parsedData = JSON.parse(localStorage.mct);
 | 
			
		||||
        const keys = Object.keys(parsedData);
 | 
			
		||||
        const lastKey = keys[keys.length - 1];
 | 
			
		||||
 | 
			
		||||
        delete parsedData[lastKey];
 | 
			
		||||
 | 
			
		||||
        //Sets local storage with missing object
 | 
			
		||||
        await page.evaluate(
 | 
			
		||||
            `window.localStorage.setItem('mct', '${JSON.stringify(parsedData)}')`
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        //Reloads page and clicks on stacked plot
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            page.reload(),
 | 
			
		||||
            page.waitForLoadState('networkidle')
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        //Verify Main section is there on load
 | 
			
		||||
        await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Stacked Plot');
 | 
			
		||||
 | 
			
		||||
        await page.locator('text=Open MCT My Items >> span').nth(3).click();
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            page.waitForNavigation(),
 | 
			
		||||
            page.locator('text=Unnamed Stacked Plot').first().click()
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        //Check that there is only one stacked item plot with a plot, the missing one will be empty
 | 
			
		||||
        await expect(page.locator(".c-plot--stacked-container:has(.gl-plot)")).toHaveCount(1);
 | 
			
		||||
        //Verify that console.warn is thrown
 | 
			
		||||
        await expect(errorLogs).toHaveLength(1);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is used the create a stacked plot object
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
async function makeStackedPlot(page) {
 | 
			
		||||
    // fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
 | 
			
		||||
    await page.goto('/', { waitUntil: 'networkidle' });
 | 
			
		||||
 | 
			
		||||
    // create stacked plot
 | 
			
		||||
    await page.locator('button.c-create-button').click();
 | 
			
		||||
    await page.locator('li:has-text("Stacked Plot")').click();
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation({ waitUntil: 'networkidle'}),
 | 
			
		||||
        page.locator('text=OK').click(),
 | 
			
		||||
        //Wait for Save Banner to appear
 | 
			
		||||
        page.waitForSelector('.c-message-banner__message')
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    //Wait until Save Banner is gone
 | 
			
		||||
    await page.locator('.c-message-banner__close-button').click();
 | 
			
		||||
    await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
 | 
			
		||||
 | 
			
		||||
    // save the stacked plot
 | 
			
		||||
    await saveStackedPlot(page);
 | 
			
		||||
 | 
			
		||||
    // create a sinewave generator
 | 
			
		||||
    await createSineWaveGenerator(page);
 | 
			
		||||
 | 
			
		||||
    // click on stacked plot
 | 
			
		||||
    await page.locator('text=Open MCT My Items >> span').nth(3).click();
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('text=Unnamed Stacked Plot').first().click()
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // create a second sinewave generator
 | 
			
		||||
    await createSineWaveGenerator(page);
 | 
			
		||||
 | 
			
		||||
    // click on stacked plot
 | 
			
		||||
    await page.locator('text=Open MCT My Items >> span').nth(3).click();
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('text=Unnamed Stacked Plot').first().click()
 | 
			
		||||
    ]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is used to save a stacked plot object
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
async function saveStackedPlot(page) {
 | 
			
		||||
    // save stacked plot
 | 
			
		||||
    await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.locator('text=Save and Finish Editing').click(),
 | 
			
		||||
        //Wait for Save Banner to appear
 | 
			
		||||
        page.waitForSelector('.c-message-banner__message')
 | 
			
		||||
    ]);
 | 
			
		||||
    //Wait until Save Banner is gone
 | 
			
		||||
    await page.locator('.c-message-banner__close-button').click();
 | 
			
		||||
    await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is used to create a sine wave generator object
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
async function createSineWaveGenerator(page) {
 | 
			
		||||
    //Create sine wave generator
 | 
			
		||||
    await page.locator('button.c-create-button').click();
 | 
			
		||||
    await page.locator('li:has-text("Sine Wave Generator")').click();
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation({ waitUntil: 'networkidle'}),
 | 
			
		||||
        page.locator('text=OK').click(),
 | 
			
		||||
        //Wait for Save Banner to appear
 | 
			
		||||
        page.waitForSelector('.c-message-banner__message')
 | 
			
		||||
    ]);
 | 
			
		||||
    //Wait until Save Banner is gone
 | 
			
		||||
    await page.locator('.c-message-banner__close-button').click();
 | 
			
		||||
    await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
const { test } = require('../../../fixtures.js');
 | 
			
		||||
// eslint-disable-next-line no-unused-vars
 | 
			
		||||
const { expect } = require('@playwright/test');
 | 
			
		||||
 | 
			
		||||
test.describe('Remote Clock', () => {
 | 
			
		||||
    // eslint-disable-next-line require-await
 | 
			
		||||
    test.fixme('blocks historical requests until first tick is received', async ({ page }) => {
 | 
			
		||||
        test.info().annotations.push({
 | 
			
		||||
            type: 'issue',
 | 
			
		||||
            description: 'https://github.com/nasa/openmct/issues/5221'
 | 
			
		||||
        });
 | 
			
		||||
        // addInitScript to with remote clock
 | 
			
		||||
        // Switch time conductor mode to 'remote clock'
 | 
			
		||||
        // Navigate to telemetry
 | 
			
		||||
        // Verify that the plot renders historical data within the correct bounds
 | 
			
		||||
        // Refresh the page
 | 
			
		||||
        // Verify again that the plot renders historical data within the correct bounds
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										184
									
								
								e2e/tests/plugins/timer/timer.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								e2e/tests/plugins/timer/timer.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
const { test } = require('../../../fixtures.js');
 | 
			
		||||
const { expect } = require('@playwright/test');
 | 
			
		||||
 | 
			
		||||
test.describe('Timer', () => {
 | 
			
		||||
 | 
			
		||||
    test.beforeEach(async ({ page }) => {
 | 
			
		||||
        //Go to baseURL
 | 
			
		||||
        await page.goto('/', { waitUntil: 'networkidle' });
 | 
			
		||||
 | 
			
		||||
        //Click the Create button
 | 
			
		||||
        await page.click('button:has-text("Create")');
 | 
			
		||||
 | 
			
		||||
        // Click 'Timer'
 | 
			
		||||
        await page.click('text=Timer');
 | 
			
		||||
 | 
			
		||||
        // Click text=OK
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            page.waitForNavigation({waitUntil: 'networkidle'}),
 | 
			
		||||
            page.click('text=OK')
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Timer');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Can perform actions on the Timer', async ({ page }) => {
 | 
			
		||||
        test.info().annotations.push({
 | 
			
		||||
            type: 'issue',
 | 
			
		||||
            description: 'https://github.com/nasa/openmct/issues/4313'
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await test.step("From the tree context menu", async () => {
 | 
			
		||||
            await triggerTimerContextMenuAction(page, 'Start');
 | 
			
		||||
            await triggerTimerContextMenuAction(page, 'Pause');
 | 
			
		||||
            await triggerTimerContextMenuAction(page, 'Restart at 0');
 | 
			
		||||
            await triggerTimerContextMenuAction(page, 'Stop');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await test.step("From the 3dot menu", async () => {
 | 
			
		||||
            await triggerTimer3dotMenuAction(page, 'Start');
 | 
			
		||||
            await triggerTimer3dotMenuAction(page, 'Pause');
 | 
			
		||||
            await triggerTimer3dotMenuAction(page, 'Restart at 0');
 | 
			
		||||
            await triggerTimer3dotMenuAction(page, 'Stop');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await test.step("From the object view", async () => {
 | 
			
		||||
            await triggerTimerViewAction(page, 'Start');
 | 
			
		||||
            await triggerTimerViewAction(page, 'Pause');
 | 
			
		||||
            await triggerTimerViewAction(page, 'Restart at 0');
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Actions that can be performed on a timer from context menus.
 | 
			
		||||
 * @typedef {'Start' | 'Stop' | 'Pause' | 'Restart at 0'} TimerAction
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Actions that can be performed on a timer from the object view.
 | 
			
		||||
 * @typedef {'Start' | 'Pause' | 'Restart at 0'} TimerViewAction
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Open the timer context menu from the object tree.
 | 
			
		||||
 * Expands the 'My Items' folder if it is not already expanded.
 | 
			
		||||
 * @param {import('@playwright/test').Page} page
 | 
			
		||||
 */
 | 
			
		||||
async function openTimerContextMenu(page) {
 | 
			
		||||
    const myItemsFolder = page.locator('text=Open MCT My Items >> span').nth(3);
 | 
			
		||||
    const className = await myItemsFolder.getAttribute('class');
 | 
			
		||||
    if (!className.includes('c-disclosure-triangle--expanded')) {
 | 
			
		||||
        await myItemsFolder.click();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await page.locator(`a:has-text("Unnamed Timer")`).click({
 | 
			
		||||
        button: 'right'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Trigger a timer action from the tree context menu
 | 
			
		||||
 * @param {import('@playwright/test').Page} page
 | 
			
		||||
 * @param {TimerAction} action
 | 
			
		||||
 */
 | 
			
		||||
async function triggerTimerContextMenuAction(page, action) {
 | 
			
		||||
    const menuAction = `.c-menu ul li >> text="${action}"`;
 | 
			
		||||
    await openTimerContextMenu(page);
 | 
			
		||||
    await page.locator(menuAction).click();
 | 
			
		||||
    assertTimerStateAfterAction(page, action);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Trigger a timer action from the 3dot menu
 | 
			
		||||
 * @param {import('@playwright/test').Page} page
 | 
			
		||||
 * @param {TimerAction} action
 | 
			
		||||
 */
 | 
			
		||||
async function triggerTimer3dotMenuAction(page, action) {
 | 
			
		||||
    const menuAction = `.c-menu ul li >> text="${action}"`;
 | 
			
		||||
    const threeDotMenuButton = 'button[title="More options"]';
 | 
			
		||||
    let isActionAvailable = false;
 | 
			
		||||
    let iterations = 0;
 | 
			
		||||
    // Dismiss/open the 3dot menu until the action is available
 | 
			
		||||
    // or a maxiumum number of iterations is reached
 | 
			
		||||
    while (!isActionAvailable && iterations <= 20) {
 | 
			
		||||
        await page.click('.c-object-view');
 | 
			
		||||
        await page.click(threeDotMenuButton);
 | 
			
		||||
        isActionAvailable = await page.locator(menuAction).isVisible();
 | 
			
		||||
        iterations++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await page.locator(menuAction).click();
 | 
			
		||||
    assertTimerStateAfterAction(page, action);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Trigger a timer action from the object view
 | 
			
		||||
 * @param {import('@playwright/test').Page} page
 | 
			
		||||
 * @param {TimerViewAction} action
 | 
			
		||||
 */
 | 
			
		||||
async function triggerTimerViewAction(page, action) {
 | 
			
		||||
    const buttonTitle = buttonTitleFromAction(action);
 | 
			
		||||
    await page.click(`button[title="${buttonTitle}"]`);
 | 
			
		||||
    assertTimerStateAfterAction(page, action);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Takes in a TimerViewAction and returns the button title
 | 
			
		||||
 * @param {TimerViewAction} action
 | 
			
		||||
 */
 | 
			
		||||
function buttonTitleFromAction(action) {
 | 
			
		||||
    switch (action) {
 | 
			
		||||
    case 'Start':
 | 
			
		||||
        return 'Start';
 | 
			
		||||
    case 'Pause':
 | 
			
		||||
        return 'Pause';
 | 
			
		||||
    case 'Restart at 0':
 | 
			
		||||
        return 'Reset';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Verify the timer state after a timer action has been performed.
 | 
			
		||||
 * @param {import('@playwright/test').Page} page
 | 
			
		||||
 * @param {TimerAction} action
 | 
			
		||||
 */
 | 
			
		||||
async function assertTimerStateAfterAction(page, action) {
 | 
			
		||||
    let timerStateClass;
 | 
			
		||||
    switch (action) {
 | 
			
		||||
    case 'Start':
 | 
			
		||||
    case 'Restart at 0':
 | 
			
		||||
        timerStateClass = "is-started";
 | 
			
		||||
        break;
 | 
			
		||||
    case 'Stop':
 | 
			
		||||
        timerStateClass = 'is-stopped';
 | 
			
		||||
        break;
 | 
			
		||||
    case 'Pause':
 | 
			
		||||
        timerStateClass = 'is-paused';
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await expect.soft(page.locator('.c-timer')).toHaveClass(new RegExp(timerStateClass));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 search 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 createClockAndDisplayLayout(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("Clock")').click();
 | 
			
		||||
    // Click button:has-text("OK")
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('button:has-text("OK")').click()
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Click a:has-text("My Items")
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('a:has-text("My Items") >> nth=0').click()
 | 
			
		||||
    ]);
 | 
			
		||||
    // Click button:has-text("Create")
 | 
			
		||||
    await page.locator('button:has-text("Create")').click();
 | 
			
		||||
    // Click li:has-text("Notebook")
 | 
			
		||||
    await page.locator('li:has-text("Display Layout")').click();
 | 
			
		||||
    // Click button:has-text("OK")
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('button:has-text("OK")').click()
 | 
			
		||||
    ]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test.describe('Grand Search', () => {
 | 
			
		||||
    test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
 | 
			
		||||
        await createClockAndDisplayLayout(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('Cl');
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
 | 
			
		||||
        // Click text=Elements >> nth=0
 | 
			
		||||
        await page.locator('text=Elements').first().click();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
 | 
			
		||||
 | 
			
		||||
        // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
 | 
			
		||||
        // Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
 | 
			
		||||
        await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
 | 
			
		||||
        await expect(page.locator('.js-preview-window')).toBeVisible();
 | 
			
		||||
 | 
			
		||||
        // Click [aria-label="Close"]
 | 
			
		||||
        await page.locator('[aria-label="Close"]').click();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).toBeVisible();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).toContainText('Cloc');
 | 
			
		||||
 | 
			
		||||
        // Click [aria-label="OpenMCT Search"] a >> nth=0
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] a').first().click();
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
 | 
			
		||||
 | 
			
		||||
        // Fill [aria-label="OpenMCT Search"] input[type="search"]
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo');
 | 
			
		||||
        await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
 | 
			
		||||
 | 
			
		||||
        // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
 | 
			
		||||
        await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
 | 
			
		||||
        // Click text=Save and Finish Editing
 | 
			
		||||
        await page.locator('text=Save and Finish Editing').click();
 | 
			
		||||
        // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
 | 
			
		||||
        // Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
 | 
			
		||||
        await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
 | 
			
		||||
        // Click text=Unnamed Clock
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            page.waitForNavigation(),
 | 
			
		||||
            page.locator('text=Unnamed Clock').click()
 | 
			
		||||
        ]);
 | 
			
		||||
        await expect(page.locator('.is-object-type-clock')).toBeVisible();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -67,7 +67,7 @@ test.describe('Visual - Default', () => {
 | 
			
		||||
        await percySnapshot(page, `About (theme: '${theme}')`);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test.fixme('Visual - Default Condition Set', async ({ page, theme }) => {
 | 
			
		||||
    test('Visual - Default Condition Set @unstable', async ({ page, theme }) => {
 | 
			
		||||
 | 
			
		||||
        await createDomainObjectWithDefaults(page, { type: 'Condition Set' });
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,7 @@ test.describe('Visual - Default', () => {
 | 
			
		||||
        await percySnapshot(page, `Default Condition Set (theme: '${theme}')`);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test.fixme('Visual - Default Condition Widget', async ({ page, theme }) => {
 | 
			
		||||
    test('Visual - Default Condition Widget @unstable', async ({ page, theme }) => {
 | 
			
		||||
        test.info().annotations.push({
 | 
			
		||||
            type: 'issue',
 | 
			
		||||
            description: 'https://github.com/nasa/openmct/issues/5349'
 | 
			
		||||
@@ -137,7 +137,7 @@ test.describe('Visual - Default', () => {
 | 
			
		||||
        await percySnapshot(page, `removed amplitude property value (theme: '${theme}')`);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test.fixme('Visual - Save Successful Banner', async ({ page, theme }) => {
 | 
			
		||||
    test('Visual - Save Successful Banner @unstable', async ({ page, theme }) => {
 | 
			
		||||
        await createDomainObjectWithDefaults(page, { type: 'Timer' });
 | 
			
		||||
 | 
			
		||||
        await page.locator('.c-message-banner__message').hover({ trial: true });
 | 
			
		||||
@@ -159,7 +159,7 @@ test.describe('Visual - Default', () => {
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test.fixme('Visual - Default Gauge is correct', async ({ page, theme }) => {
 | 
			
		||||
    test('Visual - Default Gauge is correct @unstable', async ({ page, theme }) => {
 | 
			
		||||
        await createDomainObjectWithDefaults(page, { type: 'Gauge' });
 | 
			
		||||
 | 
			
		||||
        // Take a snapshot of the newly created Gauge object
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								e2e/tests/visual/generateVisualTestData.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								e2e/tests/visual/generateVisualTestData.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 generating LocalStorage via Session Storage to be used
 | 
			
		||||
in some visual test suites like controlledClock.visual.spec.js. This suite should run to completion
 | 
			
		||||
and generate an artifact named ./e2e/test-data/VisualTestData_storage.json . This will run
 | 
			
		||||
on every Commit to ensure that this object still loads into tests correctly and will retain the
 | 
			
		||||
.e2e.spec.js suffix.
 | 
			
		||||
 | 
			
		||||
TODO: Provide additional validation of object properties as it grows.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
const { test } = require('../../fixtures.js');
 | 
			
		||||
const { expect } = require('@playwright/test');
 | 
			
		||||
 | 
			
		||||
test('Generate Visual Test Data @localStorage', async ({ page, context }) => {
 | 
			
		||||
    //Go to baseURL
 | 
			
		||||
    await page.goto('/', { waitUntil: 'networkidle' });
 | 
			
		||||
 | 
			
		||||
    await page.locator('button:has-text("Create")').click();
 | 
			
		||||
 | 
			
		||||
    // add overlay plot with defaults
 | 
			
		||||
    await page.locator('li:has-text("Overlay Plot")').click();
 | 
			
		||||
 | 
			
		||||
    // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
 | 
			
		||||
    await page.click('form[name="mctForm"] a:has-text("My Items")');
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('text=OK').click(),
 | 
			
		||||
        //Wait for Save Banner to appear1
 | 
			
		||||
        page.waitForSelector('.c-message-banner__message')
 | 
			
		||||
    ]);
 | 
			
		||||
    //Wait until Save Banner is gone
 | 
			
		||||
    await page.locator('.c-message-banner__close-button').click();
 | 
			
		||||
    await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
 | 
			
		||||
 | 
			
		||||
    // save (exit edit mode)
 | 
			
		||||
    await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
 | 
			
		||||
    await page.locator('text=Save and Finish Editing').click();
 | 
			
		||||
 | 
			
		||||
    // click create button
 | 
			
		||||
    await page.locator('button:has-text("Create")').click();
 | 
			
		||||
 | 
			
		||||
    // add sine wave generator with defaults
 | 
			
		||||
    await page.locator('li:has-text("Sine Wave Generator")').click();
 | 
			
		||||
 | 
			
		||||
    //Add a 5000 ms Delay
 | 
			
		||||
    await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
 | 
			
		||||
 | 
			
		||||
    // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
 | 
			
		||||
    await page.click('form[name="mctForm"] a:has-text("Overlay Plot")');
 | 
			
		||||
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('text=OK').click(),
 | 
			
		||||
        //Wait for Save Banner to appear1
 | 
			
		||||
        page.waitForSelector('.c-message-banner__message')
 | 
			
		||||
    ]);
 | 
			
		||||
    //Wait until Save Banner is gone
 | 
			
		||||
    await page.locator('.c-message-banner__close-button').click();
 | 
			
		||||
    await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
 | 
			
		||||
 | 
			
		||||
    // focus the overlay plot
 | 
			
		||||
    await page.locator('text=Open MCT My Items >> span').nth(3).click();
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        page.waitForNavigation(),
 | 
			
		||||
        page.locator('text=Unnamed Overlay Plot').first().click()
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
 | 
			
		||||
    //Save localStorage for future test execution
 | 
			
		||||
    await context.storageState({ path: './e2e/test-data/VisualTestData_storage.json' });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										22
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								package.json
									
									
									
									
									
								
							@@ -5,7 +5,7 @@
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@babel/eslint-parser": "7.18.9",
 | 
			
		||||
    "@braintree/sanitize-url": "6.0.0",
 | 
			
		||||
    "@percy/cli": "1.8.1",
 | 
			
		||||
    "@percy/cli": "1.10.0",
 | 
			
		||||
    "@percy/playwright": "1.0.4",
 | 
			
		||||
    "@playwright/test": "1.23.0",
 | 
			
		||||
    "@types/eventemitter3": "^1.0.0",
 | 
			
		||||
@@ -23,9 +23,9 @@
 | 
			
		||||
    "d3-axis": "3.0.0",
 | 
			
		||||
    "d3-scale": "3.3.0",
 | 
			
		||||
    "d3-selection": "3.0.0",
 | 
			
		||||
    "eslint": "8.18.0",
 | 
			
		||||
    "eslint": "8.22.0",
 | 
			
		||||
    "eslint-plugin-compat": "4.0.2",
 | 
			
		||||
    "eslint-plugin-playwright": "0.10.0",
 | 
			
		||||
    "eslint-plugin-playwright": "0.11.1",
 | 
			
		||||
    "eslint-plugin-vue": "9.3.0",
 | 
			
		||||
    "eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
 | 
			
		||||
    "eventemitter3": "1.2.0",
 | 
			
		||||
@@ -33,9 +33,8 @@
 | 
			
		||||
    "file-saver": "2.0.5",
 | 
			
		||||
    "git-rev-sync": "3.0.2",
 | 
			
		||||
    "html2canvas": "1.4.1",
 | 
			
		||||
    "imports-loader": "0.8.0",
 | 
			
		||||
    "imports-loader": "4.0.1",
 | 
			
		||||
    "jasmine-core": "4.3.0",
 | 
			
		||||
    "jsdoc": "3.6.11",
 | 
			
		||||
    "karma": "6.3.20",
 | 
			
		||||
    "karma-chrome-launcher": "3.1.1",
 | 
			
		||||
    "karma-cli": "2.0.0",
 | 
			
		||||
@@ -46,22 +45,20 @@
 | 
			
		||||
    "karma-sourcemap-loader": "0.3.8",
 | 
			
		||||
    "karma-spec-reporter": "0.0.34",
 | 
			
		||||
    "karma-webpack": "5.0.0",
 | 
			
		||||
    "lighthouse": "9.6.1",
 | 
			
		||||
    "location-bar": "3.0.1",
 | 
			
		||||
    "lodash": "4.17.21",
 | 
			
		||||
    "mini-css-extract-plugin": "2.6.1",
 | 
			
		||||
    "moment": "2.29.4",
 | 
			
		||||
    "moment-duration-format": "2.3.2",
 | 
			
		||||
    "moment-timezone": "0.5.34",
 | 
			
		||||
    "node-bourbon": "4.2.3",
 | 
			
		||||
    "moment-timezone": "0.5.37",
 | 
			
		||||
    "nyc":"15.1.0",
 | 
			
		||||
    "painterro": "1.2.78",
 | 
			
		||||
    "plotly.js-basic-dist": "2.12.0",
 | 
			
		||||
    "plotly.js-gl2d-dist": "2.12.0",
 | 
			
		||||
    "plotly.js-basic-dist": "2.14.0",
 | 
			
		||||
    "plotly.js-gl2d-dist": "2.14.0",
 | 
			
		||||
    "printj": "1.3.1",
 | 
			
		||||
    "request": "2.88.2",
 | 
			
		||||
    "resolve-url-loader": "5.0.0",
 | 
			
		||||
    "sass": "1.52.2",
 | 
			
		||||
    "sass": "1.54.4",
 | 
			
		||||
    "sass-loader": "13.0.2",
 | 
			
		||||
    "sinon": "14.0.0",
 | 
			
		||||
    "style-loader": "^1.0.1",
 | 
			
		||||
@@ -99,11 +96,8 @@
 | 
			
		||||
    "test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
 | 
			
		||||
    "test:perf": "npx playwright test --config=e2e/playwright-performance.config.js",
 | 
			
		||||
    "test:watch": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
 | 
			
		||||
    "update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2022/gm' ./src/ui/layout/AboutDialog.vue",
 | 
			
		||||
    "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2022/gm'",
 | 
			
		||||
    "otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
 | 
			
		||||
    "docs": "npm run jsdoc ; npm run otherdoc",
 | 
			
		||||
    "cov:e2e:report":"nyc report --reporter=lcovonly --report-dir=./coverage/e2e",
 | 
			
		||||
    "cov:e2e:full:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full",
 | 
			
		||||
    "cov:e2e:stable:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-stable",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import FormProperties from './components/FormProperties.vue';
 | 
			
		||||
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
export default class FormsAPI extends EventEmitter {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
@@ -158,7 +159,8 @@ export default class FormsAPI extends EventEmitter {
 | 
			
		||||
                    key = property.join('.');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                changes[key] = data.value;
 | 
			
		||||
                _.set(changes, `${key}`, data.value);
 | 
			
		||||
                self.emit('formPropertiesChanged', changes);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import PropertiesAction from './PropertiesAction';
 | 
			
		||||
import CreateWizard from './CreateWizard';
 | 
			
		||||
 | 
			
		||||
import { v4 as uuid } from 'uuid';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
export default class CreateAction extends PropertiesAction {
 | 
			
		||||
    constructor(openmct, type, parentDomainObject) {
 | 
			
		||||
@@ -50,19 +51,15 @@ export default class CreateAction extends PropertiesAction {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const properties = key.split('.');
 | 
			
		||||
            let object = this.domainObject;
 | 
			
		||||
            const propertiesLength = properties.length;
 | 
			
		||||
            properties.forEach((property, index) => {
 | 
			
		||||
                const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
 | 
			
		||||
                if (isComplexProperty && object[property] !== null) {
 | 
			
		||||
                    object = object[property];
 | 
			
		||||
                } else {
 | 
			
		||||
                    object[property] = value;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            const existingValue = this.domainObject[`${key}`];
 | 
			
		||||
            if (!(existingValue instanceof Array) && (typeof existingValue === 'object')) {
 | 
			
		||||
                value = {
 | 
			
		||||
                    ...existingValue,
 | 
			
		||||
                    ...value
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            object = value;
 | 
			
		||||
            _.set(this.domainObject, `${key}`, value);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const parentDomainObject = parentDomainObjectPath[0];
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
 | 
			
		||||
import PropertiesAction from './PropertiesAction';
 | 
			
		||||
import CreateWizard from './CreateWizard';
 | 
			
		||||
 | 
			
		||||
export default class EditPropertiesAction extends PropertiesAction {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        super(openmct);
 | 
			
		||||
@@ -54,23 +55,26 @@ export default class EditPropertiesAction extends PropertiesAction {
 | 
			
		||||
    _onSave(changes) {
 | 
			
		||||
        try {
 | 
			
		||||
            Object.entries(changes).forEach(([key, value]) => {
 | 
			
		||||
                const properties = key.split('.');
 | 
			
		||||
                let object = this.domainObject;
 | 
			
		||||
                const propertiesLength = properties.length;
 | 
			
		||||
                properties.forEach((property, index) => {
 | 
			
		||||
                    const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
 | 
			
		||||
                    if (isComplexProperty && object[property] !== null) {
 | 
			
		||||
                        object = object[property];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        object[property] = value;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                const existingValue = this.domainObject[`${key}`];
 | 
			
		||||
                if (!(existingValue instanceof Array) && (typeof existingValue === 'object')) {
 | 
			
		||||
                    value = {
 | 
			
		||||
                        ...existingValue,
 | 
			
		||||
                        ...value
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                object = value;
 | 
			
		||||
                this.openmct.objects.mutate(this.domainObject, key, value);
 | 
			
		||||
                this.openmct.notifications.info('Save successful');
 | 
			
		||||
                this.openmct.objects.mutate(this.domainObject, `${key}`, value);
 | 
			
		||||
            });
 | 
			
		||||
            if (this.openmct.editor.isEditing()) {
 | 
			
		||||
                this.openmct.editor.save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.openmct.notifications.info('Save successful');
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (this.openmct.editor.isEditing()) {
 | 
			
		||||
                this.openmct.editor.cancel();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.openmct.notifications.error('Error saving objects');
 | 
			
		||||
            console.error(error);
 | 
			
		||||
        }
 | 
			
		||||
@@ -81,6 +85,9 @@ export default class EditPropertiesAction extends PropertiesAction {
 | 
			
		||||
     */
 | 
			
		||||
    _onCancel() {
 | 
			
		||||
        //noop
 | 
			
		||||
        if (this.openmct.editor.isEditing()) {
 | 
			
		||||
            this.openmct.editor.cancel();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -92,6 +99,12 @@ export default class EditPropertiesAction extends PropertiesAction {
 | 
			
		||||
        const createWizard = new CreateWizard(this.openmct, this.domainObject, objectPath[1]);
 | 
			
		||||
        const formStructure = createWizard.getFormStructure(false);
 | 
			
		||||
        formStructure.title = 'Edit ' + this.domainObject.name;
 | 
			
		||||
        //If we're in edit mode AND want to edit properties for the same domain object
 | 
			
		||||
        //In this case, saving will put the object in view-only mode
 | 
			
		||||
        //TODO: Maybe we should block editing properties if someone is in Edit mode?
 | 
			
		||||
        if (!this.openmct.editor.isEditing()) {
 | 
			
		||||
            this.openmct.editor.edit();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.openmct.forms.showForm(formStructure)
 | 
			
		||||
            .then(this._onSave.bind(this))
 | 
			
		||||
 
 | 
			
		||||
@@ -151,6 +151,7 @@ describe('EditPropertiesAction plugin', () => {
 | 
			
		||||
        function callback(newObject) {
 | 
			
		||||
            expect(newObject.name).not.toEqual(oldName);
 | 
			
		||||
            expect(newObject.name).toEqual(newName);
 | 
			
		||||
            expect(openmct.editor.isEditing()).toBeFalse();
 | 
			
		||||
 | 
			
		||||
            unObserve();
 | 
			
		||||
            done();
 | 
			
		||||
@@ -164,6 +165,7 @@ describe('EditPropertiesAction plugin', () => {
 | 
			
		||||
        openmct.forms.on('onFormPropertyChange', deBouncedFormChange);
 | 
			
		||||
 | 
			
		||||
        function handleFormPropertyChange(data) {
 | 
			
		||||
            expect(openmct.editor.isEditing()).toBeTrue();
 | 
			
		||||
            const form = document.querySelector('.js-form');
 | 
			
		||||
            const title = form.querySelector('input');
 | 
			
		||||
            const notes = form.querySelector('textArea');
 | 
			
		||||
@@ -213,10 +215,12 @@ describe('EditPropertiesAction plugin', () => {
 | 
			
		||||
        editPropertiesAction.invoke([domainObject])
 | 
			
		||||
            .then(() => {
 | 
			
		||||
                expect(domainObject.name).toEqual(name);
 | 
			
		||||
                expect(openmct.editor.isEditing()).toBeFalse();
 | 
			
		||||
                done();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(() => {
 | 
			
		||||
                expect(domainObject.name).toEqual(name);
 | 
			
		||||
                expect(openmct.editor.isEditing()).toBeFalse();
 | 
			
		||||
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -100,6 +100,7 @@ export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        ToggleSwitch
 | 
			
		||||
    },
 | 
			
		||||
    inject: ["openmct"],
 | 
			
		||||
    props: {
 | 
			
		||||
        model: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
@@ -107,6 +108,8 @@ export default {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        this.changes = {};
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            isUseTelemetryLimits: this.model.value.isUseTelemetryLimits,
 | 
			
		||||
            isDisplayMinMax: this.model.value.isDisplayMinMax,
 | 
			
		||||
@@ -118,21 +121,30 @@ export default {
 | 
			
		||||
            min: this.model.value.min
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.openmct.forms.on('formPropertiesChanged', this.onFormPropertyChange);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.openmct.forms.off('formPropertiesChanged', this.onFormPropertyChange);
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        onFormPropertyChange(data) {
 | 
			
		||||
            this.changes = data?.configuration?.gaugeController || {};
 | 
			
		||||
        },
 | 
			
		||||
        onChange(event) {
 | 
			
		||||
            const data = {
 | 
			
		||||
                model: this.model,
 | 
			
		||||
                value: {
 | 
			
		||||
                    gaugeType: this.model.value.gaugeType,
 | 
			
		||||
                    isDisplayMinMax: this.isDisplayMinMax,
 | 
			
		||||
                    isDisplayCurVal: this.isDisplayCurVal,
 | 
			
		||||
                    isDisplayUnits: this.isDisplayUnits,
 | 
			
		||||
                    gaugeType: this.changes.gaugeType || this.model.value.gaugeType,
 | 
			
		||||
                    isDisplayMinMax: this.changes.isDisplayMinMax === undefined ? this.isDisplayMinMax : this.changes.isDisplayMinMax,
 | 
			
		||||
                    isDisplayCurVal: this.changes.isDisplayCurVal === undefined ? this.isDisplayCurVal : this.changes.isDisplayCurVal,
 | 
			
		||||
                    isDisplayUnits: this.changes.isDisplayUnits === undefined ? this.isDisplayUnits : this.changes.isDisplayUnits,
 | 
			
		||||
                    isUseTelemetryLimits: this.isUseTelemetryLimits,
 | 
			
		||||
                    limitLow: this.limitLow,
 | 
			
		||||
                    limitHigh: this.limitHigh,
 | 
			
		||||
                    max: this.max,
 | 
			
		||||
                    min: this.min,
 | 
			
		||||
                    precision: this.model.value.precision
 | 
			
		||||
                    precision: this.changes.precision === undefined ? this.model.value.precision : this.changes.precision
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,10 @@
 | 
			
		||||
    <template v-if="!page.isLocked">
 | 
			
		||||
        <div
 | 
			
		||||
            class="c-list__item__name js-list__item__name"
 | 
			
		||||
            :class="[{ 'c-input-inline': isSelected }]"
 | 
			
		||||
            :data-id="page.id"
 | 
			
		||||
            :contenteditable="true"
 | 
			
		||||
            :contenteditable="isSelected"
 | 
			
		||||
            @keydown.escape="updateName"
 | 
			
		||||
            @keydown.enter="updateName"
 | 
			
		||||
            @blur="updateName"
 | 
			
		||||
        >{{ pageName }}</div>
 | 
			
		||||
@@ -32,8 +34,9 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import PopupMenu from './PopupMenu.vue';
 | 
			
		||||
import { KEY_ENTER, KEY_ESCAPE } from '../utils/notebook-key-code';
 | 
			
		||||
import RemoveDialog from '../utils/removeDialog';
 | 
			
		||||
import PopupMenu from './PopupMenu.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
@@ -107,36 +110,39 @@ export default {
 | 
			
		||||
            removeDialog.show();
 | 
			
		||||
        },
 | 
			
		||||
        selectPage(event) {
 | 
			
		||||
            const target = event.target;
 | 
			
		||||
            const id = target.dataset.id;
 | 
			
		||||
            const { target: { dataset: { id } } } = event;
 | 
			
		||||
 | 
			
		||||
            if (!this.page.isLocked) {
 | 
			
		||||
                const page = target.closest('.js-list__item');
 | 
			
		||||
                const input = page.querySelector('.js-list__item__name');
 | 
			
		||||
 | 
			
		||||
                if (page.className.indexOf('is-selected') > -1) {
 | 
			
		||||
                    input.classList.add('c-input-inline');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!id) {
 | 
			
		||||
            if (this.isSelected || !id) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('selectPage', id);
 | 
			
		||||
        },
 | 
			
		||||
        updateName(event) {
 | 
			
		||||
            const target = event.target;
 | 
			
		||||
            const name = target.textContent.toString();
 | 
			
		||||
            target.classList.remove('c-input-inline');
 | 
			
		||||
 | 
			
		||||
            if (name === '' || this.page.name === name) {
 | 
			
		||||
        renamePage(target) {
 | 
			
		||||
            if (!target) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('renamePage', Object.assign(this.page, { name }));
 | 
			
		||||
            target.textContent = target.textContent ? target.textContent.trim() : `Unnamed ${this.pageTitle}`;
 | 
			
		||||
 | 
			
		||||
            if (this.page.name === target.textContent) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('renamePage', Object.assign(this.page, { name: target.textContent }));
 | 
			
		||||
        },
 | 
			
		||||
        updateName(event) {
 | 
			
		||||
            const { target, keyCode, type } = event;
 | 
			
		||||
 | 
			
		||||
            if (keyCode === KEY_ESCAPE) {
 | 
			
		||||
                target.textContent = this.page.name;
 | 
			
		||||
            } else if (keyCode === KEY_ENTER || type === 'blur') {
 | 
			
		||||
                this.renamePage(target);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            target.scrollLeft = '0';
 | 
			
		||||
 | 
			
		||||
            target.blur();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,10 @@
 | 
			
		||||
>
 | 
			
		||||
    <span
 | 
			
		||||
        class="c-list__item__name js-list__item__name"
 | 
			
		||||
        :class="[{ 'c-input-inline': isSelected && !section.isLocked }]"
 | 
			
		||||
        :data-id="section.id"
 | 
			
		||||
        contenteditable="true"
 | 
			
		||||
        :contenteditable="isSelected && !section.isLocked"
 | 
			
		||||
        @keydown.escape="updateName"
 | 
			
		||||
        @keydown.enter="updateName"
 | 
			
		||||
        @blur="updateName"
 | 
			
		||||
    >{{ sectionName }}</span>
 | 
			
		||||
@@ -20,8 +22,9 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import PopupMenu from './PopupMenu.vue';
 | 
			
		||||
import { KEY_ENTER, KEY_ESCAPE } from '../utils/notebook-key-code';
 | 
			
		||||
import RemoveDialog from '../utils/removeDialog';
 | 
			
		||||
import PopupMenu from './PopupMenu.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
@@ -96,36 +99,39 @@ export default {
 | 
			
		||||
            removeDialog.show();
 | 
			
		||||
        },
 | 
			
		||||
        selectSection(event) {
 | 
			
		||||
            const target = event.target;
 | 
			
		||||
            const id = target.dataset.id;
 | 
			
		||||
            const { target: { dataset: { id } } } = event;
 | 
			
		||||
 | 
			
		||||
            if (!this.section.isLocked) {
 | 
			
		||||
                const section = target.closest('.js-list__item');
 | 
			
		||||
                const input = section.querySelector('.js-list__item__name');
 | 
			
		||||
 | 
			
		||||
                if (section.className.indexOf('is-selected') > -1) {
 | 
			
		||||
                    input.classList.add('c-input-inline');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!id) {
 | 
			
		||||
            if (this.isSelected || !id) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('selectSection', id);
 | 
			
		||||
        },
 | 
			
		||||
        updateName(event) {
 | 
			
		||||
            const target = event.target;
 | 
			
		||||
            target.classList.remove('c-input-inline');
 | 
			
		||||
            const name = target.textContent.trim();
 | 
			
		||||
 | 
			
		||||
            if (name === '' || this.section.name === name) {
 | 
			
		||||
        renameSection(target) {
 | 
			
		||||
            if (!target) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('renameSection', Object.assign(this.section, { name }));
 | 
			
		||||
            target.textContent = target.textContent ? target.textContent.trim() : `Unnamed ${this.sectionTitle}`;
 | 
			
		||||
 | 
			
		||||
            if (this.section.name === target.textContent) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.$emit('renameSection', Object.assign(this.section, { name: target.textContent }));
 | 
			
		||||
        },
 | 
			
		||||
        updateName(event) {
 | 
			
		||||
            const { target, keyCode, type } = event;
 | 
			
		||||
 | 
			
		||||
            if (keyCode === KEY_ESCAPE) {
 | 
			
		||||
                target.textContent = this.section.name;
 | 
			
		||||
            } else if (keyCode === KEY_ENTER || type === 'blur') {
 | 
			
		||||
                this.renameSection(target);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            target.scrollLeft = '0';
 | 
			
		||||
 | 
			
		||||
            target.blur();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,7 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__name {
 | 
			
		||||
            flex: 0 1 auto;
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__menu-indicator {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/plugins/notebook/utils/notebook-key-code.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/plugins/notebook/utils/notebook-key-code.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// Key codes for `KeyboardEvent.keyCode`.
 | 
			
		||||
export const KEY_ENTER = 13;
 | 
			
		||||
export const KEY_ESCAPE = 27;
 | 
			
		||||
@@ -83,6 +83,18 @@ create_admin_user () {
 | 
			
		||||
    curl -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/admins/$COUCH_ADMIN_USER -d \'"$COUCH_ADMIN_PASSWORD"\'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
is_cors_enabled() {
 | 
			
		||||
    resource_exists $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/httpd/enable_cors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enable_cors () {
 | 
			
		||||
    curl -su "${CURL_USERPASS_ARG}" -o /dev/null -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/httpd/enable_cors -d '"true"'
 | 
			
		||||
    curl -su "${CURL_USERPASS_ARG}" -o /dev/null -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/cors/origins -d '"http://localhost:8080"'
 | 
			
		||||
    curl -su "${CURL_USERPASS_ARG}" -o /dev/null -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/cors/credentials -d '"true"'
 | 
			
		||||
    curl -su "${CURL_USERPASS_ARG}" -o /dev/null -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/cors/methods -d '"GET, PUT, POST, HEAD, DELETE"'
 | 
			
		||||
    curl -su "${CURL_USERPASS_ARG}" -o /dev/null -X PUT $COUCH_BASE_LOCAL/_node/$COUCH_NODE_NAME/_config/cors/headers -d '"accept, authorization, content-type, origin, referer, x-csrf-token"'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [ "$(admin_user_exists)" == "FALSE" ]; then
 | 
			
		||||
    echo "Admin user does not exist, creating..."
 | 
			
		||||
    create_admin_user
 | 
			
		||||
@@ -123,3 +135,10 @@ if [ "{\"ok\":true}" == "${response}" ]; then
 | 
			
		||||
else
 | 
			
		||||
    echo "Database permissions not updated"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "FALSE" == $(is_cors_enabled) ]; then
 | 
			
		||||
    echo "Enabling CORS"
 | 
			
		||||
    enable_cors
 | 
			
		||||
else
 | 
			
		||||
    echo "CORS enabled, nothing to do"
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@
 | 
			
		||||
                    <span v-if="showValueWhenExpanded">Value</span>
 | 
			
		||||
                    <span v-if="showMinimumWhenExpanded">Min</span>
 | 
			
		||||
                    <span v-if="showMaximumWhenExpanded">Max</span>
 | 
			
		||||
                    <span v-if="showUnitsWhenExpanded">Units</span>
 | 
			
		||||
                    <span v-if="showUnitsWhenExpanded">Unit</span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@
 | 
			
		||||
                <option value="nearestValue">Nearest value</option>
 | 
			
		||||
                <option value="min">Minimum value</option>
 | 
			
		||||
                <option value="max">Maximum value</option>
 | 
			
		||||
                <option value="units">Units</option>
 | 
			
		||||
                <option value="unit">Unit</option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </li>
 | 
			
		||||
@@ -89,7 +89,7 @@
 | 
			
		||||
                    v-model="showUnitsWhenExpanded"
 | 
			
		||||
                    type="checkbox"
 | 
			
		||||
                    @change="updateForm('showUnitsWhenExpanded')"
 | 
			
		||||
                > Units</li>
 | 
			
		||||
                > Unit</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@
 | 
			
		||||
        <span class="plot-series-name">{{ nameWithUnit }}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div
 | 
			
		||||
        v-show="!!highlights.length && (valueToShowWhenCollapsed !== 'none' && valueToShowWhenCollapsed !== 'units')"
 | 
			
		||||
        v-show="!!highlights.length && (valueToShowWhenCollapsed !== 'none' && valueToShowWhenCollapsed !== 'unit')"
 | 
			
		||||
        class="plot-series-value hover-value-enabled"
 | 
			
		||||
        :class="[{ 'cursor-hover': notNearest }, valueToDisplayWhenCollapsedClass, mctLimitStateClass]"
 | 
			
		||||
    >
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,6 @@ define([
 | 
			
		||||
    './condition/plugin',
 | 
			
		||||
    './conditionWidget/plugin',
 | 
			
		||||
    './themes/espresso',
 | 
			
		||||
    './themes/maelstrom',
 | 
			
		||||
    './themes/snow',
 | 
			
		||||
    './URLTimeSettingsSynchronizer/plugin',
 | 
			
		||||
    './notificationIndicator/plugin',
 | 
			
		||||
@@ -122,7 +121,6 @@ define([
 | 
			
		||||
    ConditionPlugin,
 | 
			
		||||
    ConditionWidgetPlugin,
 | 
			
		||||
    Espresso,
 | 
			
		||||
    Maelstrom,
 | 
			
		||||
    Snow,
 | 
			
		||||
    URLTimeSettingsSynchronizer,
 | 
			
		||||
    NotificationIndicator,
 | 
			
		||||
@@ -207,7 +205,6 @@ define([
 | 
			
		||||
    plugins.ClearData = ClearData;
 | 
			
		||||
    plugins.WebPage = WebPagePlugin.default;
 | 
			
		||||
    plugins.Espresso = Espresso.default;
 | 
			
		||||
    plugins.Maelstrom = Maelstrom.default;
 | 
			
		||||
    plugins.Snow = Snow.default;
 | 
			
		||||
    plugins.Condition = ConditionPlugin.default;
 | 
			
		||||
    plugins.ConditionWidget = ConditionWidgetPlugin.default;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
@import "../../styles/vendor/normalize-min";
 | 
			
		||||
@import "../../styles/constants";
 | 
			
		||||
@import "../../styles/constants-mobile.scss";
 | 
			
		||||
 | 
			
		||||
@import "../../styles/constants-maelstrom";
 | 
			
		||||
 | 
			
		||||
@import "../../styles/mixins";
 | 
			
		||||
@import "../../styles/animations";
 | 
			
		||||
@import "../../styles/about";
 | 
			
		||||
@import "../../styles/glyphs";
 | 
			
		||||
@import "../../styles/global";
 | 
			
		||||
@import "../../styles/status";
 | 
			
		||||
@import "../../styles/limits";
 | 
			
		||||
@import "../../styles/controls";
 | 
			
		||||
@import "../../styles/forms";
 | 
			
		||||
@import "../../styles/table";
 | 
			
		||||
@import "../../styles/legacy";
 | 
			
		||||
@import "../../styles/legacy-plots";
 | 
			
		||||
@import "../../styles/plotly";
 | 
			
		||||
@import "../../styles/legacy-messages";
 | 
			
		||||
 | 
			
		||||
@import "../../styles/vue-styles.scss";
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
import { installTheme } from './installTheme';
 | 
			
		||||
 | 
			
		||||
export default function plugin() {
 | 
			
		||||
    return function install(openmct) {
 | 
			
		||||
        installTheme(openmct, 'maelstrom');
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -744,3 +744,9 @@ body.mobile {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-list__item {
 | 
			
		||||
    &__name:focus {
 | 
			
		||||
        text-overflow: clip;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ const config = {
 | 
			
		||||
        inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js',
 | 
			
		||||
        espressoTheme: './src/plugins/themes/espresso-theme.scss',
 | 
			
		||||
        snowTheme: './src/plugins/themes/snow-theme.scss',
 | 
			
		||||
        maelstromTheme: './src/plugins/themes/maelstrom-theme.scss'
 | 
			
		||||
    },
 | 
			
		||||
    output: {
 | 
			
		||||
        globalObject: 'this',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user