[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:
John Hill 2024-10-04 18:47:25 -04:00 committed by GitHub
parent 55c023d1eb
commit 47f0b66c7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 98 additions and 39 deletions

View File

@ -8,8 +8,8 @@ executors:
- image: mcr.microsoft.com/playwright:v1.47.2-focal
environment:
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_LOGLEVEL: "debug" # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
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_PARALLEL_TOTAL: 2
ubuntu:
machine:
@ -17,7 +17,7 @@ executors:
docker_layer_caching: true
commands:
build_and_install:
description: "All steps used to build and install."
description: 'All steps used to build and install.'
parameters:
node-version:
type: string
@ -27,7 +27,7 @@ commands:
node-version: << parameters.node-version >>
- node/install-packages
generate_and_store_version_and_filesystem_artifacts:
description: "Track important packages and files"
description: 'Track important packages and files'
steps:
- run: |
[[ $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
./codecov --help
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:
suite:
type: string
@ -135,13 +135,13 @@ jobs:
suite: #ci or full
type: string
executor: pw-focal-development
parallelism: 7
parallelism: 8
steps:
- build_and_install:
node-version: lts/hydrogen
- when: #Only install chrome-beta when running the 'full' suite to save $$$
condition:
equal: ["full", <<parameters.suite>>]
equal: ['full', <<parameters.suite>>]
steps:
- run: npx playwright install chrome-beta
- run:
@ -323,7 +323,7 @@ workflows:
- e2e-couchdb
triggers:
- schedule:
cron: "0 0 * * *"
cron: '0 0 * * *'
filters:
branches:
only:

View File

@ -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
* 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 {
createDomainObjectWithDefaults,
createExampleTelemetryObject,
createNotification,
createPlanFromJSON,
createStableStateTelemetry,
expandEntireTree,
getCanvasPixels,
linkParameterToObject,
navigateToObjectWithFixedTimeBounds,
navigateToObjectWithRealTime,
setEndOffset,

View File

@ -26,8 +26,10 @@ import {
createExampleTelemetryObject,
createNotification,
createPlanFromJSON,
createStableStateTelemetry,
expandEntireTree,
getCanvasPixels,
linkParameterToObject,
navigateToObjectWithFixedTimeBounds,
navigateToObjectWithRealTime,
setEndOffset,
@ -339,4 +341,23 @@ test.describe('AppActions @framework', () => {
// Expect this step to fail
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();
});
});

View File

@ -23,7 +23,11 @@
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 { expect, test } from '../../../../pluginFixtures.js';
@ -92,7 +96,7 @@ test.describe('Conditionally Styling, using a Condition Set', () => {
await page.getByLabel('Save').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
await page.goto(displayLayout.url, { waitUntil: 'domcontentloaded' });
@ -157,21 +161,3 @@ async function waitForStyleChange(element, expectedStyle, timeout = 0) {
expect(style).toBe(expectedStyle);
}).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();
}

View File

@ -22,7 +22,11 @@
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 { test } from '../../pluginFixtures.js';
@ -47,16 +51,13 @@ test.describe('Visual - Display Layout @clock', () => {
name: 'Child Right Layout',
parent: parentLayout.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 1',
parent: child1Layout.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 2',
parent: child2Layout.uuid
});
const stableStateTelemetry = await createStableStateTelemetry(page);
await linkParameterToObject(page, stableStateTelemetry.name, child1Layout.name);
await linkParameterToObject(page, stableStateTelemetry.name, child2Layout.name);
// Pause the clock at a time where the telemetry is stable 20 minutes in the future
await page.clock.pauseAt(new Date(MISSION_TIME + 1200000));
await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: 'Edit Object' }).click();