Skip to content

Commit

Permalink
2.0.3 merge to master (nasa#5157)
Browse files Browse the repository at this point in the history
* Release 2.0.3

* Fix tick values for plots ticks in log mode and null check (nasa#5119)

* [2297] When there is no display range or range, skip setting the range value when auto scale is turned off.

* If the formatted value is a number and a float, set precision to 2 decimal points.

* Fix value assignment

* Use whole numbers in log mode

* Revert whole numbers fix - need floats for values between 0 and 1.

* Handle scrolling to focused image on resize/new data (nasa#5121)

* Scroll to focused image when view resizes - this will force scrolling to focused image when going to/from view large mode

* Scroll to the right if there is no paused focused image

* [LAD Tables] Use Telemetry Collections (nasa#5127)

* Use telemetry collections to handle bounds checks

* added telemetry collection to alphanumeric telemetry view (nasa#5131)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage (nasa#5116)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage

* Remove animation from POS and CAM

* Fix transactions overwriting latest objects with stale objects on save (nasa#5132)

* use object (map) instead of set to track dirty objects
* fix tests due to internals change

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>

* Gauge edit enabled 2.0.3 (nasa#5133)

* Gauge plugin nasa#4896, add edit mode

* Dynamic dial-type Gauge sizing by height and width (nasa#5129)

* Improve sizing strategy for gauges.
* Do not install gauge by default for now

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>

* [Telemetry Collections] Include data with start and end bounds (nasa#5145)

* Reverts forced precision for log plots axis labels (nasa#5147)

* Condition Widgets trigger hundreds of persistence calls (nasa#5146)

Co-authored-by: unlikelyzero <jchill2@gmail.com>

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Michael Rogers <contact@mhrogers.com>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
  • Loading branch information
8 people authored May 3, 2022
1 parent b77a406 commit c089a47
Show file tree
Hide file tree
Showing 23 changed files with 695 additions and 428 deletions.
172 changes: 145 additions & 27 deletions e2e/tests/plugins/condition/condition.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,163 @@
*****************************************************************************/

/*
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
suite is sharing state between tests which is considered an anti-pattern. Implimenting in this way to
demonstrate some playwright for test developers. This pattern should not be re-used in other CRUD suites.
*/

const { test, expect } = require('@playwright/test');

test.describe('Condition Set Operations', () => {
test('Create new button `condition set` creates new condition object', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
let conditionSetUrl;
let getConditionSetIdentifierFromUrl;

test('Create new Condition Set object and store @localStorage', async ({ page, context }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });

//Click the Create button
await page.click('button:has-text("Create")');

// Click text=Condition Set
await page.click('text=Condition Set');

// Click text=OK
await Promise.all([
page.waitForNavigation(),
page.click('text=OK')
]);

await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
//Save localStorage for future test execution
await context.storageState({ path: './e2e/tests/recycled_storage.json' });

//Set object identifier from url
conditionSetUrl = await page.url();
console.log('conditionSetUrl ' + conditionSetUrl);

getConditionSetIdentifierFromUrl = await conditionSetUrl.split('/').pop().split('?')[0];
console.log('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);

});

test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//Load localStorage for subsequent tests
test.use({ storageState: './e2e/tests/recycled_storage.json' });

//Begin suite of tests again localStorage
test('Condition set object properties persist in main view and inspector', async ({ page }) => {
//Navigate to baseURL with injected localStorage
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });

//Click the Create button
await page.click('button:has-text("Create")');
//Assertions on loaded Condition Set in main view
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');

// Click text=Condition Set
await page.click('text=Condition Set');
//Assertions on loaded Condition Set in Inspector
await expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy;

// Click text=OK
//Reload Page
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/dab945d4-5a84-480e-8180-222b4aa730fa?tc.mode=fixed&tc.startBound=1639696164435&tc.endBound=1639697964435&tc.timeSystem=utc&view=conditionSet.view' }*/),
page.click('text=OK')
page.reload(),
page.waitForLoadState('networkidle')
]);

await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
});
test.fixme('condition set object properties exist', async ({ page }) => {
//Go to object created in step one
//Verify the Condition Set properties persist on Save
//Verify the Condition Set properties persist on page.reload()
//Re-verify after reload
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
//Assertions on loaded Condition Set in Inspector
await expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy;

});
test.fixme('condition set object can be modified', async ({ page }) => {
//Go to object created in step one
test('condition set object can be modified on @localStorage', async ({ page }) => {
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });

//Assertions on loaded Condition Set in main view
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');

//Update the Condition Set properties
//Verify the Condition Set properties persist on Save
//Verify the Condition Set properties persist on page.reload()
// Click Edit Button
await page.locator('text=Conditions View Snapshot >> button').nth(3).click();

//Edit Condition Set Name from main view
await page.locator('text=Unnamed Condition Set').first().fill('Renamed Condition Set');
await page.locator('text=Renamed Condition Set').first().press('Enter');
// Click Save Button
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
// Click Save and Finish Editing Option
await page.locator('text=Save and Finish Editing').click();

//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');

// Verify Inspector properties
// Verify Inspector has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
// Verify Inspector Details has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();

// Verify Tree reflects updated Name proprety
// Expand Tree
await page.locator('text=Open MCT My Items >> span >> nth=3').click();
// Verify Condition Set Object is renamed in Tree
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
// Verify Search Tree reflects renamed Name property
await page.locator('input[type="search"]').fill('Renamed');
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();

//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);

//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');

// Verify Inspector properties
// Verify Inspector has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
// Verify Inspector Details has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();

// Verify Tree reflects updated Name proprety
// Expand Tree
await page.locator('text=Open MCT My Items >> span >> nth=3').click();
// Verify Condition Set Object is renamed in Tree
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
// Verify Search Tree reflects renamed Name property
await page.locator('input[type="search"]').fill('Renamed');
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
});
test.fixme('condition set object can be deleted', async ({ page }) => {
//Go to object created in step one
//Verify that Condition Set object can be deleted
//Verify the Condition Set object does not exist in Tree
//Verify the Condition Set object does not exist with direct navigation to object's URL
test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({ page }) => {
//Navigate to baseURL
await page.goto('/', { waitUntil: 'networkidle' });

//Expect Unnamed Condition Set to be visible in Main View
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set")')).toBeVisible();

// Search for Unnamed Condition Set
await page.locator('input[type="search"]').fill('Unnamed Condition Set');
// Right Click to Open Actions Menu
await page.locator('a:has-text("Unnamed Condition Set")').click({
button: 'right'
});
// Click Remove Action
await page.locator('text=Remove').click();

await page.locator('text=OK').click();

//Expect Unnamed Condition Set to be removed in Main View
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set")')).not.toBeVisible();

await page.locator('.c-search__clear-input').click();
// Search for Unnamed Condition Set
await page.locator('input[type="search"]').fill('Unnamed Condition Set');
// Expect Unnamed Condition Set to be removed
await expect(page.locator('a:has-text("Unnamed Condition Set")')).not.toBeVisible();

//Feature?
//Domain Object is still available by direct URL after delete
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');

});
});
22 changes: 22 additions & 0 deletions e2e/tests/recycled_storage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:8080",
"localStorage": [
{
"name": "tcHistory",
"value": "{\"utc\":[{\"start\":1651513945533,\"end\":1651515745533}]}"
},
{
"name": "mct",
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1651515746374,\"modified\":1651515746374},\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"e35a066b-eb0e-4b05-a4c9-cc31dc202572\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1651515746373,\"location\":\"mine\",\"persisted\":1651515746373}}"
},
{
"name": "mct-tree-expanded",
"value": "[]"
}
]
}
]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "2.0.3-SNAPSHOT",
"version": "2.0.3",
"description": "The Open MCT core platform",
"devDependencies": {
"@babel/eslint-parser": "7.16.3",
Expand Down
2 changes: 0 additions & 2 deletions src/MCT.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ define([
this.branding = BrandingAPI.default;

// Plugins that are installed by default

this.install(this.plugins.Gauge());
this.install(this.plugins.Plot());
this.install(this.plugins.Chart());
this.install(this.plugins.TelemetryTable.default());
Expand Down
23 changes: 13 additions & 10 deletions src/api/objects/Transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@

export default class Transaction {
constructor(objectAPI) {
this.dirtyObjects = new Set();
this.dirtyObjects = {};
this.objectAPI = objectAPI;
}

add(object) {
this.dirtyObjects.add(object);
const key = this.objectAPI.makeKeyString(object.identifier);

this.dirtyObjects[key] = object;
}

cancel() {
Expand All @@ -37,7 +39,8 @@ export default class Transaction {
commit() {
const promiseArray = [];
const save = this.objectAPI.save.bind(this.objectAPI);
this.dirtyObjects.forEach(object => {

Object.values(this.dirtyObjects).forEach(object => {
promiseArray.push(this.createDirtyObjectPromise(object, save));
});

Expand All @@ -48,7 +51,9 @@ export default class Transaction {
return new Promise((resolve, reject) => {
action(object)
.then((success) => {
this.dirtyObjects.delete(object);
const key = this.objectAPI.makeKeyString(object.identifier);

delete this.dirtyObjects[key];
resolve(success);
})
.catch(reject);
Expand All @@ -57,7 +62,8 @@ export default class Transaction {

getDirtyObject(identifier) {
let dirtyObject;
this.dirtyObjects.forEach(object => {

Object.values(this.dirtyObjects).forEach(object => {
const areIdsEqual = this.objectAPI.areIdsEqual(object.identifier, identifier);
if (areIdsEqual) {
dirtyObject = object;
Expand All @@ -67,14 +73,11 @@ export default class Transaction {
return dirtyObject;
}

start() {
this.dirtyObjects = new Set();
}

_clear() {
const promiseArray = [];
const refresh = this.objectAPI.refresh.bind(this.objectAPI);
this.dirtyObjects.forEach(object => {

Object.values(this.dirtyObjects).forEach(object => {
promiseArray.push(this.createDirtyObjectPromise(object, refresh));
});

Expand Down
16 changes: 8 additions & 8 deletions src/api/objects/TransactionSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,37 +34,37 @@ describe("Transaction Class", () => {
});

it('has no dirty objects', () => {
expect(transaction.dirtyObjects.size).toEqual(0);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
});

it('add(), adds object to dirtyObjects', () => {
const mockDomainObjects = createMockDomainObjects();
transaction.add(mockDomainObjects[0]);
expect(transaction.dirtyObjects.size).toEqual(1);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(1);
});

it('cancel(), clears all dirtyObjects', (done) => {
const mockDomainObjects = createMockDomainObjects(3);
mockDomainObjects.forEach(transaction.add.bind(transaction));

expect(transaction.dirtyObjects.size).toEqual(3);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(3);

transaction.cancel()
.then(success => {
expect(transaction.dirtyObjects.size).toEqual(0);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
}).finally(done);
});

it('commit(), saves all dirtyObjects', (done) => {
const mockDomainObjects = createMockDomainObjects(3);
mockDomainObjects.forEach(transaction.add.bind(transaction));

expect(transaction.dirtyObjects.size).toEqual(3);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(3);
spyOn(objectAPI, 'save').and.callThrough();

transaction.commit()
.then(success => {
expect(transaction.dirtyObjects.size).toEqual(0);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
expect(objectAPI.save.calls.count()).toEqual(3);
}).finally(done);
});
Expand All @@ -73,7 +73,7 @@ describe("Transaction Class", () => {
const mockDomainObjects = createMockDomainObjects();
transaction.add(mockDomainObjects[0]);

expect(transaction.dirtyObjects.size).toEqual(1);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(1);
const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier);

expect(dirtyObject).toEqual(mockDomainObjects[0]);
Expand All @@ -82,7 +82,7 @@ describe("Transaction Class", () => {
it('getDirtyObject(), returns empty dirtyObject for no active transaction', () => {
const mockDomainObjects = createMockDomainObjects();

expect(transaction.dirtyObjects.size).toEqual(0);
expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier);

expect(dirtyObject).toEqual(undefined);
Expand Down
4 changes: 2 additions & 2 deletions src/api/telemetry/TelemetryCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ export class TelemetryCollection extends EventEmitter {

for (let datum of data) {
parsedValue = this.parseTime(datum);
beforeStartOfBounds = parsedValue <= this.lastBounds.start;
afterEndOfBounds = parsedValue >= this.lastBounds.end;
beforeStartOfBounds = parsedValue < this.lastBounds.start;
afterEndOfBounds = parsedValue > this.lastBounds.end;

if (!afterEndOfBounds && !beforeStartOfBounds) {
let isDuplicate = false;
Expand Down
Loading

0 comments on commit c089a47

Please sign in to comment.