Skip to content

[e2e] Update to quickstart fixure, add makefile, and add tests Mct7343 and 7198 #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
43 changes: 43 additions & 0 deletions tests/e2e/framework/quickstartFixtures.e2e.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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 testing our use of our custom fixtures to verify
that they are working as expected.
*/

import { expect, request, test} from '../quickstartFixtures.js';

test.describe('quickstartFixtures tests', () => {
test('filterNonFetchRequests', async ({ page }) => {
// Listening for all network requests and pushing them into allNetworkRequests array.
page.on('request', request => allNetworkRequests.push(request));

// Testing the initial page load and verifying the presence of specific elements.
await page.goto("./", { waitUntil: "networkidle" });
fetchRequests = filterNonFetchRequests(allNetworkRequests);
expect(fetchRequests.length).toBe(4);

// Removing the 'request' event listener to prevent potential memory leaks.
page.removeListener('request', request => allNetworkRequests.push(request));
});
});
2 changes: 1 addition & 1 deletion tests/e2e/playwright-quickstart.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const config = {
command: 'npm run start:coverage',
url: 'http://localhost:9000/#',
timeout: 120 * 1000,
reuseExistingServer: false
reuseExistingServer: true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

driveby. We're moving away from 'false' for faster debugging

},
workers: 1,
projects: [
Expand Down
53 changes: 53 additions & 0 deletions tests/e2e/quickstartFixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable no-undef */
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/

/**
* The file contains custom fixtures which extend the base functionality of the Playwright fixtures
* and appActions. These fixtures should be generalized across all plugins.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will become the prototypical fixtures file that we expect openmct-e2e-as-a-dependency users to follow. They will source pluginFixture.js to set their 'My Items' per deployment

*/

// import { createDomainObjectWithDefaults } from './appActions.js';
import { test, request, expect } from './opensource/pluginFixtures.js';

/**
* The name of the "My Items" folder in the domain object tree.
*
* Default: `"My Items"`
*
* @type {string}
*/
export const myItemsFolderName = 'My Items';

export { expect, request, test };

/**
* Filters out non-fetch requests from the given array of network requests.
* This includes preflight CORS, fetching stylesheets, page icons, etc.
* Requires that a page requests object be instantiated.
* page.on('request', request => allNetworkRequests.push(request));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need better jsdoc

* @param {Array} requests - Array of network requests to filter.
* @returns {Array} Filtered network requests.
*/
export function filterNonFetchRequests(requests) {
return requests.filter(request => request.resourceType() === 'fetch');
}
131 changes: 131 additions & 0 deletions tests/e2e/yamcs/actions.e2e.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*
* Network Specific Tests for Open MCT and YAMCS connectivity.
* This suite verifies the network requests made by the application to ensure correct interaction with YAMCS.
*/

import { test, expect, filterNonFetchRequests } from '../quickstartFixtures.js';
import { createDomainObjectWithDefaults, expandEntireTree, setFixedTimeMode } from '../opensource/appActions.js';

test.describe('Reload action', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });

const displayLayout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout'
});

const alphaTable = await createDomainObjectWithDefaults(page, {
type: 'Telemetry Table',
name: 'Alpha Table'
});

await page.getByLabel('Expand myproject folder').click();
await page.getByLabel('Expand myproject folder').click();
await page.locator('.c-table__body-w').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('treeitem', { name: 'Battery1_Temp' }).dragTo(page.locator('.c-table__body-w'));

// Save (exit edit mode)
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();

const betaTable = await createDomainObjectWithDefaults(page, {
type: 'Telemetry Table',
name: 'Beta Table'
});

await page.locator('.c-table__body-w').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('treeitem', { name: 'Battery1_Voltage' }).dragTo(page.locator('.c-table__body-w'));

// Save (exit edit mode)
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();

await page.goto(displayLayout.url);



await page.getByLabel('Edit Object', { exact: true }).click();

await page.dragAndDrop(`text='Alpha Table'`, '.l-layout__grid-holder', {
targetPosition: { x: 0, y: 0 }
});

await page.dragAndDrop(`text='Beta Table'`, '.l-layout__grid-holder', {
targetPosition: { x: 0, y: 250 }
});

await page.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
});

test.only('can reload display layout and its children', async ({ page }) => {
const beforeReloadAlphaTelemetryValue = await page
.getByLabel('Alpha Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');
const beforeReloadBetaTelemetryValue = await page
.getByLabel('Beta Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');
// reload alpha
await page.getByTitle('View menu items').first().click();
await page.getByRole('menuitem', { name: /Reload/ }).click();

const afterReloadAlphaTelemetryValue = await page
.getByLabel('Alpha Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');
const afterReloadBetaTelemetryValue = await page
.getByLabel('Beta Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');

expect(beforeReloadAlphaTelemetryValue).not.toEqual(afterReloadAlphaTelemetryValue);
expect(beforeReloadBetaTelemetryValue).toEqual(afterReloadBetaTelemetryValue);

// now reload parent
await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: /Reload/ }).click();

const fullReloadAlphaTelemetryValue = await page
.getByLabel('Alpha Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');
const fullReloadBetaTelemetryValue = await page
.getByLabel('Beta Table table content')
.getByLabel('wavelengths table cell')
.first()
.getAttribute('title');

expect(fullReloadAlphaTelemetryValue).not.toEqual(afterReloadAlphaTelemetryValue);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These earlier tests were predicated on the sinewave generator, that created new telemetry on every page load. If we're going to use YAMCS telemetry, we can't rely on the telemetry changing underneath us on reload.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long and short of it: I think we should get rid of these expects

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And instead add some network measuring on reload. I think this will be the best to see if the reload actually performed as expected

Copy link
Collaborator

@scottbell scottbell Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still makes sense 9 months later to me 😀

expect(fullReloadBetaTelemetryValue).not.toEqual(afterReloadBetaTelemetryValue);
});
});
42 changes: 21 additions & 21 deletions tests/e2e/yamcs/network.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ import { setFixedTimeMode } from '../opensource/appActions.js';
*/
test.describe("Quickstart network requests @yamcs", () => {
// Keeping track of network requests during the tests.
let networkRequests = [];
let filteredRequests = [];
let allNetworkRequests = [];
let fetchRequests = [];

// These variables hold the promises for specific network requests we expect to occur.
let parameterArchiveGet, batchGetStaleness, allParams, userGet, mdbOverride, mdbGet;

test('Validate network traffic to YAMCS', async ({ page }) => {
// Listening for all network requests and pushing them into networkRequests array.
page.on('request', request => networkRequests.push(request));
// Listening for all network requests and pushing them into allNetworkRequests array.
page.on('request', request => allNetworkRequests.push(request));

// Setting up promises to wait for specific network responses.
networkRequests = [];
allNetworkRequests = [];
mdbGet = page.waitForResponse('**/api/mdb/myproject/space-systems');
allParams = page.waitForResponse('**/api/mdb/myproject/parameters?details=yes&limit=1000');
userGet = page.waitForResponse('**/api/user/');
Expand All @@ -52,8 +52,8 @@ test.describe("Quickstart network requests @yamcs", () => {
// Testing the initial page load and verifying the presence of specific elements.
await page.goto("./", { waitUntil: "networkidle" });
await Promise.all([mdbGet, allParams, userGet, mdbOverride]);
filteredRequests = filterNonFetchRequests(networkRequests);
expect(filteredRequests.length).toBe(4);
fetchRequests = filterNonFetchRequests(allNetworkRequests);
expect(fetchRequests.length).toBe(4);

// I'm not sure what is going on here
const myProjectTreeItem = page.locator('.c-tree__item').filter({ hasText: 'myproject' });
Expand All @@ -63,35 +63,35 @@ test.describe("Quickstart network requests @yamcs", () => {

// More UI interactions and network request verifications.
await page.waitForLoadState('networkidle');
networkRequests = [];
allNetworkRequests = [];
batchGetStaleness = page.waitForResponse('**/api/processors/myproject/realtime/parameters:batchGet');
await page.getByRole('treeitem', { name: 'Expand CCSDS_Packet_Sequence' }).click();
await batchGetStaleness;

await page.waitForLoadState('networkidle');
expect(networkRequests.length).toBe(1);
expect(allNetworkRequests.length).toBe(1);

// Further UI interactions and network requests verification.
networkRequests = [];
allNetworkRequests = [];
parameterArchiveGet = page.waitForResponse('**/api/archive/myproject/parameters/myproject/CCSDS_Packet_Length/samples**');
batchGetStaleness = page.waitForResponse('**/api/processors/myproject/realtime/parameters:batchGet');
await page.getByRole('treeitem', { name: 'CCSDS_Packet_Length' }).click();

await Promise.all([parameterArchiveGet, batchGetStaleness]);

await page.waitForLoadState('networkidle');
expect(networkRequests.length).toBe(2);
expect(allNetworkRequests.length).toBe(2);

// Simulating the change to fixed time mode and validating network requests.
networkRequests = [];
allNetworkRequests = [];
parameterArchiveGet = page.waitForResponse('**/api/archive/myproject/parameters/myproject/CCSDS_Packet_Length/samples**');
await setFixedTimeMode(page);
await page.waitForLoadState('networkidle');
await parameterArchiveGet;
expect(networkRequests.length).toBe(1);
expect(allNetworkRequests.length).toBe(1);

// Clicking on a different telemetry item to generate new requests.
networkRequests = [];
allNetworkRequests = [];
let groupFlagsGet = page.waitForResponse('**/api/archive/myproject/parameters/myproject/CCSDS_Packet_Sequence.GroupFlags**');
let countGet = page.waitForResponse('**/api/archive/myproject/parameters/myproject/CCSDS_Packet_Sequence.Count**');
batchGetStaleness = page.waitForResponse('**/api/processors/myproject/realtime/parameters:batchGet');
Expand All @@ -101,10 +101,10 @@ test.describe("Quickstart network requests @yamcs", () => {

await Promise.all([groupFlagsGet, countGet, batchGetStaleness]);

expect(networkRequests.length).toBe(3);
expect(allNetworkRequests.length).toBe(3);

// Clicking on the telemetry item in Fixed Time mode to generate two requests.
networkRequests = [];
allNetworkRequests = [];
parameterArchiveGet = page.waitForResponse('**/api/archive/myproject/parameters/myproject/CCSDS_Packet_Length/samples**');
batchGetStaleness = page.waitForResponse('**/api/processors/myproject/realtime/parameters:batchGet');
await page.getByRole('treeitem', { name: 'CCSDS_Packet_Length' }).click();
Expand All @@ -113,10 +113,10 @@ test.describe("Quickstart network requests @yamcs", () => {
await Promise.all([parameterArchiveGet, batchGetStaleness]);

// Waiting for debounced requests in YAMCS Latest Telemetry Provider to finish.
expect(networkRequests.length).toBe(2);
expect(allNetworkRequests.length).toBe(2);

// Simulating a page refresh to generate a sequence of network requests.
networkRequests = [];
allNetworkRequests = [];
userGet = page.waitForResponse('**/api/user/');
allParams = page.waitForResponse('**/api/mdb/myproject/parameters?details=yes&limit=1000');
mdbOverride = page.waitForResponse('**/api/mdb-overrides/myproject/realtime');
Expand All @@ -128,11 +128,11 @@ test.describe("Quickstart network requests @yamcs", () => {
await Promise.all([allParams, userGet, mdbOverride, parameterArchiveGet, batchGetStaleness, mdbOverride]);

// Waiting for debounced requests in YAMCS Latest Telemetry Provider to finish.
filteredRequests = filterNonFetchRequests(networkRequests);
expect(filteredRequests.length).toBe(6);
fetchRequests = filterNonFetchRequests(allNetworkRequests);
expect(fetchRequests.length).toBe(6);

// Removing the 'request' event listener to prevent potential memory leaks.
page.removeListener('request', request => networkRequests.push(request));
page.removeListener('request', request => allNetworkRequests.push(request));
});

/**
Expand Down