mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
[CI] Fix flake with clocks and state generators (#7867)
* add two appActions * replace with appAction * replace with determinsitic appAction * fix lint * speed
This commit is contained in:
parent
55c023d1eb
commit
47f0b66c7e
@ -8,8 +8,8 @@ executors:
|
|||||||
- image: mcr.microsoft.com/playwright:v1.47.2-focal
|
- image: mcr.microsoft.com/playwright:v1.47.2-focal
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||||
PERCY_POSTINSTALL_BROWSER: "true" # Needed to store the percy browser in cache deps
|
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
|
||||||
PERCY_LOGLEVEL: "debug" # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
|
PERCY_LOGLEVEL: 'debug' # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
|
||||||
PERCY_PARALLEL_TOTAL: 2
|
PERCY_PARALLEL_TOTAL: 2
|
||||||
ubuntu:
|
ubuntu:
|
||||||
machine:
|
machine:
|
||||||
@ -17,7 +17,7 @@ executors:
|
|||||||
docker_layer_caching: true
|
docker_layer_caching: true
|
||||||
commands:
|
commands:
|
||||||
build_and_install:
|
build_and_install:
|
||||||
description: "All steps used to build and install."
|
description: 'All steps used to build and install.'
|
||||||
parameters:
|
parameters:
|
||||||
node-version:
|
node-version:
|
||||||
type: string
|
type: string
|
||||||
@ -27,7 +27,7 @@ commands:
|
|||||||
node-version: << parameters.node-version >>
|
node-version: << parameters.node-version >>
|
||||||
- node/install-packages
|
- node/install-packages
|
||||||
generate_and_store_version_and_filesystem_artifacts:
|
generate_and_store_version_and_filesystem_artifacts:
|
||||||
description: "Track important packages and files"
|
description: 'Track important packages and files'
|
||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
[[ $EUID -ne 0 ]] && (sudo mkdir -p /tmp/artifacts && sudo chmod 777 /tmp/artifacts) || (mkdir -p /tmp/artifacts && chmod 777 /tmp/artifacts)
|
[[ $EUID -ne 0 ]] && (sudo mkdir -p /tmp/artifacts && sudo chmod 777 /tmp/artifacts) || (mkdir -p /tmp/artifacts && chmod 777 /tmp/artifacts)
|
||||||
@ -61,7 +61,7 @@ commands:
|
|||||||
[[ $EUID -ne 0 ]] && sudo chmod +x codecov || chmod +x codecov
|
[[ $EUID -ne 0 ]] && sudo chmod +x codecov || chmod +x codecov
|
||||||
./codecov --help
|
./codecov --help
|
||||||
generate_e2e_code_cov_report:
|
generate_e2e_code_cov_report:
|
||||||
description: "Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test"
|
description: 'Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test'
|
||||||
parameters:
|
parameters:
|
||||||
suite:
|
suite:
|
||||||
type: string
|
type: string
|
||||||
@ -135,13 +135,13 @@ jobs:
|
|||||||
suite: #ci or full
|
suite: #ci or full
|
||||||
type: string
|
type: string
|
||||||
executor: pw-focal-development
|
executor: pw-focal-development
|
||||||
parallelism: 7
|
parallelism: 8
|
||||||
steps:
|
steps:
|
||||||
- build_and_install:
|
- build_and_install:
|
||||||
node-version: lts/hydrogen
|
node-version: lts/hydrogen
|
||||||
- when: #Only install chrome-beta when running the 'full' suite to save $$$
|
- when: #Only install chrome-beta when running the 'full' suite to save $$$
|
||||||
condition:
|
condition:
|
||||||
equal: ["full", <<parameters.suite>>]
|
equal: ['full', <<parameters.suite>>]
|
||||||
steps:
|
steps:
|
||||||
- run: npx playwright install chrome-beta
|
- run: npx playwright install chrome-beta
|
||||||
- run:
|
- run:
|
||||||
@ -323,7 +323,7 @@ workflows:
|
|||||||
- e2e-couchdb
|
- e2e-couchdb
|
||||||
triggers:
|
triggers:
|
||||||
- schedule:
|
- schedule:
|
||||||
cron: "0 0 * * *"
|
cron: '0 0 * * *'
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
@ -227,6 +227,37 @@ async function createExampleTelemetryObject(page, parent = 'mine') {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Stable State Telemetry Object (State Generator) for use in visual tests
|
||||||
|
* and tests against plotting telemetry (e.g. logPlot tests). This will change state every 2 seconds.
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string | import('../src/api/objects/ObjectAPI').Identifier} [parent] the uuid or identifier of the parent object. Defaults to 'mine'
|
||||||
|
* @returns {Promise<CreatedObjectInfo>} An object containing information about the telemetry object.
|
||||||
|
*/
|
||||||
|
async function createStableStateTelemetry(page, parent = 'mine') {
|
||||||
|
const parentUrl = await getHashUrlToDomainObject(page, parent);
|
||||||
|
|
||||||
|
await page.goto(`${parentUrl}`);
|
||||||
|
const createdObject = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'State Generator',
|
||||||
|
name: 'Stable State Generator'
|
||||||
|
});
|
||||||
|
// edit the state generator to have a 1 second update rate
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
|
||||||
|
await page.getByLabel('State Duration (seconds)', { exact: true }).fill('2');
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
// Wait until the URL is updated
|
||||||
|
const uuid = await getFocusedObjectUuid(page);
|
||||||
|
const url = await getHashUrlToDomainObject(page, uuid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: createdObject.name,
|
||||||
|
uuid,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates directly to a given object url, in fixed time mode, with the given start and end bounds. Note: does not set
|
* Navigates directly to a given object url, in fixed time mode, with the given start and end bounds. Note: does not set
|
||||||
* default view type.
|
* default view type.
|
||||||
@ -629,13 +660,33 @@ async function getCanvasPixels(page, canvasSelector) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for telemetry and link it to an object. objectName should come from the domainObject.name function.
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} parameterName
|
||||||
|
* @param {string} objectName
|
||||||
|
*/
|
||||||
|
async function linkParameterToObject(page, parameterName, objectName) {
|
||||||
|
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||||
|
await page.getByRole('searchbox', { name: 'Search Input' }).fill(parameterName);
|
||||||
|
await page.getByLabel('Object Results').getByText(parameterName).click();
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByLabel('Create Link').click();
|
||||||
|
await page.getByLabel('Modal Overlay').getByLabel('Search Input').click();
|
||||||
|
await page.getByLabel('Modal Overlay').getByLabel('Search Input').fill(objectName);
|
||||||
|
await page.getByLabel('Modal Overlay').getByLabel(`Navigate to ${objectName}`).click();
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createExampleTelemetryObject,
|
createExampleTelemetryObject,
|
||||||
createNotification,
|
createNotification,
|
||||||
createPlanFromJSON,
|
createPlanFromJSON,
|
||||||
|
createStableStateTelemetry,
|
||||||
expandEntireTree,
|
expandEntireTree,
|
||||||
getCanvasPixels,
|
getCanvasPixels,
|
||||||
|
linkParameterToObject,
|
||||||
navigateToObjectWithFixedTimeBounds,
|
navigateToObjectWithFixedTimeBounds,
|
||||||
navigateToObjectWithRealTime,
|
navigateToObjectWithRealTime,
|
||||||
setEndOffset,
|
setEndOffset,
|
||||||
|
@ -26,8 +26,10 @@ import {
|
|||||||
createExampleTelemetryObject,
|
createExampleTelemetryObject,
|
||||||
createNotification,
|
createNotification,
|
||||||
createPlanFromJSON,
|
createPlanFromJSON,
|
||||||
|
createStableStateTelemetry,
|
||||||
expandEntireTree,
|
expandEntireTree,
|
||||||
getCanvasPixels,
|
getCanvasPixels,
|
||||||
|
linkParameterToObject,
|
||||||
navigateToObjectWithFixedTimeBounds,
|
navigateToObjectWithFixedTimeBounds,
|
||||||
navigateToObjectWithRealTime,
|
navigateToObjectWithRealTime,
|
||||||
setEndOffset,
|
setEndOffset,
|
||||||
@ -339,4 +341,23 @@ test.describe('AppActions @framework', () => {
|
|||||||
// Expect this step to fail
|
// Expect this step to fail
|
||||||
await waitForPlotsToRender(page, { timeout: 1000 });
|
await waitForPlotsToRender(page, { timeout: 1000 });
|
||||||
});
|
});
|
||||||
|
test('createStableStateTelemetry', async ({ page }) => {
|
||||||
|
const stableStateTelemetry = await createStableStateTelemetry(page);
|
||||||
|
expect(stableStateTelemetry.name).toBe('Stable State Generator');
|
||||||
|
expect(stableStateTelemetry.url).toBe(`./#/browse/mine/${stableStateTelemetry.uuid}`);
|
||||||
|
expect(stableStateTelemetry.uuid).toBeDefined();
|
||||||
|
});
|
||||||
|
test('linkParameterToObject', async ({ page }) => {
|
||||||
|
const displayLayout = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Display Layout',
|
||||||
|
name: 'Test Display Layout'
|
||||||
|
});
|
||||||
|
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||||
|
|
||||||
|
await linkParameterToObject(page, exampleTelemetry.name, displayLayout.name);
|
||||||
|
await page.goto(displayLayout.url);
|
||||||
|
await expect(page.getByRole('main').getByText('Test Display Layout')).toBeVisible();
|
||||||
|
await expandEntireTree(page);
|
||||||
|
await expect(page.getByLabel('Navigate to VIPER Rover').first()).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,11 @@
|
|||||||
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets and styling
|
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets and styling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, setRealTimeMode } from '../../../../appActions.js';
|
import {
|
||||||
|
createDomainObjectWithDefaults,
|
||||||
|
linkParameterToObject,
|
||||||
|
setRealTimeMode
|
||||||
|
} from '../../../../appActions.js';
|
||||||
import { MISSION_TIME } from '../../../../constants.js';
|
import { MISSION_TIME } from '../../../../constants.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
|
|
||||||
@ -92,7 +96,7 @@ test.describe('Conditionally Styling, using a Condition Set', () => {
|
|||||||
await page.getByLabel('Save').click();
|
await page.getByLabel('Save').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
await searchAndLinkParameterToObject(page, stateGenerator.name, displayLayout.name);
|
await linkParameterToObject(page, stateGenerator.name, displayLayout.name);
|
||||||
|
|
||||||
//Add a box to the display layout
|
//Add a box to the display layout
|
||||||
await page.goto(displayLayout.url, { waitUntil: 'domcontentloaded' });
|
await page.goto(displayLayout.url, { waitUntil: 'domcontentloaded' });
|
||||||
@ -157,21 +161,3 @@ async function waitForStyleChange(element, expectedStyle, timeout = 0) {
|
|||||||
expect(style).toBe(expectedStyle);
|
expect(style).toBe(expectedStyle);
|
||||||
}).toPass({ timeout: 1000 }); // timeout allows for the style to be applied
|
}).toPass({ timeout: 1000 }); // timeout allows for the style to be applied
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for telemetry and link it to an object. objectName should come from the domainObject.name function.
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
* @param {string} parameterName
|
|
||||||
* @param {string} objectName
|
|
||||||
*/
|
|
||||||
async function searchAndLinkParameterToObject(page, parameterName, objectName) {
|
|
||||||
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
|
||||||
await page.getByRole('searchbox', { name: 'Search Input' }).fill(parameterName);
|
|
||||||
await page.getByLabel('Object Results').getByText(parameterName).click();
|
|
||||||
await page.getByLabel('More actions').click();
|
|
||||||
await page.getByLabel('Create Link').click();
|
|
||||||
await page.getByLabel('Modal Overlay').getByLabel('Search Input').click();
|
|
||||||
await page.getByLabel('Modal Overlay').getByLabel('Search Input').fill(objectName);
|
|
||||||
await page.getByLabel('Modal Overlay').getByLabel(`Navigate to ${objectName}`).click();
|
|
||||||
await page.getByLabel('Save').click();
|
|
||||||
}
|
|
||||||
|
@ -22,7 +22,11 @@
|
|||||||
|
|
||||||
import percySnapshot from '@percy/playwright';
|
import percySnapshot from '@percy/playwright';
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults } from '../../appActions.js';
|
import {
|
||||||
|
createDomainObjectWithDefaults,
|
||||||
|
createStableStateTelemetry,
|
||||||
|
linkParameterToObject
|
||||||
|
} from '../../appActions.js';
|
||||||
import { MISSION_TIME, VISUAL_FIXED_URL } from '../../constants.js';
|
import { MISSION_TIME, VISUAL_FIXED_URL } from '../../constants.js';
|
||||||
import { test } from '../../pluginFixtures.js';
|
import { test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
@ -47,16 +51,13 @@ test.describe('Visual - Display Layout @clock', () => {
|
|||||||
name: 'Child Right Layout',
|
name: 'Child Right Layout',
|
||||||
parent: parentLayout.uuid
|
parent: parentLayout.uuid
|
||||||
});
|
});
|
||||||
await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Sine Wave Generator',
|
const stableStateTelemetry = await createStableStateTelemetry(page);
|
||||||
name: 'SWG 1',
|
await linkParameterToObject(page, stableStateTelemetry.name, child1Layout.name);
|
||||||
parent: child1Layout.uuid
|
await linkParameterToObject(page, stableStateTelemetry.name, child2Layout.name);
|
||||||
});
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
// Pause the clock at a time where the telemetry is stable 20 minutes in the future
|
||||||
type: 'Sine Wave Generator',
|
await page.clock.pauseAt(new Date(MISSION_TIME + 1200000));
|
||||||
name: 'SWG 2',
|
|
||||||
parent: child2Layout.uuid
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' });
|
await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' });
|
||||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||||
|
Loading…
Reference in New Issue
Block a user