mirror of
https://github.com/nasa/openmct.git
synced 2025-05-31 22:50:49 +00:00
[e2e] Clean up example imagery tests, fix display layout test (#5685)
* Clean up imagery tests, fix display layout test (cherry picked from commit 7d6699fcecb34cf98bac4aa8f9a1b2996736325c) * Code review comments, more cleanup * Add Tabs View suite, extract common actions to functions * Add missing await * remove unnecessary wait * lint * [e2e] playwright v1.25.0 -->v1.25.2 Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
parent
a7ea5afa59
commit
15ab0dae50
@ -2,7 +2,7 @@ version: 2.1
|
|||||||
executors:
|
executors:
|
||||||
pw-focal-development:
|
pw-focal-development:
|
||||||
docker:
|
docker:
|
||||||
- image: mcr.microsoft.com/playwright:v1.25.0-focal
|
- image: mcr.microsoft.com/playwright:v1.25.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
|
||||||
|
2
.github/workflows/e2e-couchdb.yml
vendored
2
.github/workflows/e2e-couchdb.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
- run: npx playwright@1.25.0 install
|
- run: npx playwright@1.25.2 install
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
|
- run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
|
||||||
- run: npm run test:e2e:couchdb
|
- run: npm run test:e2e:couchdb
|
||||||
|
2
.github/workflows/e2e-pr.yml
vendored
2
.github/workflows/e2e-pr.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
- run: npx playwright@1.25.0 install
|
- run: npx playwright@1.25.2 install
|
||||||
- run: npx playwright install chrome-beta
|
- run: npx playwright install chrome-beta
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm run test:e2e:full
|
- run: npm run test:e2e:full
|
||||||
|
@ -35,45 +35,24 @@ const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' :
|
|||||||
|
|
||||||
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
|
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
|
||||||
test.describe('Example Imagery Object', () => {
|
test.describe('Example Imagery Object', () => {
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
// Create a default 'Example Imagery' object
|
// Create a default 'Example Imagery' object
|
||||||
createDomainObjectWithDefaults(page, { type: 'Example Imagery' });
|
await createDomainObjectWithDefaults(page, { type: 'Example Imagery' });
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator(backgroundImageSelector).hover({trial: true}),
|
|
||||||
// eslint-disable-next-line playwright/missing-playwright-await
|
|
||||||
expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Verify that the created object is focused
|
// Verify that the created object is focused
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||||
|
await page.locator(backgroundImageSelector).hover({trial: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
||||||
const deltaYStep = 100; //equivalent to 1x zoom
|
// Zoom in x2 and assert
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
await mouseZoomOnImageAndAssert(page, 2);
|
||||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
// zoom in
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
|
||||||
// wait for zoom animation to finish
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
// zoom out
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
await page.mouse.wheel(0, -deltaYStep);
|
|
||||||
// wait for zoom animation to finish
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
|
|
||||||
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
|
// Zoom out x2 and assert
|
||||||
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
|
await mouseZoomOnImageAndAssert(page, -2);
|
||||||
expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height);
|
|
||||||
expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can adjust image brightness/contrast by dragging the sliders', async ({ page, browserName }) => {
|
test('Can adjust image brightness/contrast by dragging the sliders', async ({ page, browserName }) => {
|
||||||
@ -151,30 +130,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
||||||
// Get initial image dimensions
|
await buttonZoomOnImageAndAssert(page);
|
||||||
const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
|
|
||||||
// Zoom in twice via button
|
|
||||||
await zoomIntoImageryByButton(page);
|
|
||||||
await zoomIntoImageryByButton(page);
|
|
||||||
|
|
||||||
// Get and assert zoomed in image dimensions
|
|
||||||
const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
|
||||||
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
|
||||||
|
|
||||||
// Zoom out once via button
|
|
||||||
await zoomOutOfImageryByButton(page);
|
|
||||||
|
|
||||||
// Get and assert zoomed out image dimensions
|
|
||||||
const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
|
||||||
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
|
||||||
|
|
||||||
// Zoom out again via button, assert against the initial image dimensions
|
|
||||||
await zoomOutOfImageryByButton(page);
|
|
||||||
const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use the reset button to reset the image @unstable', async ({ page }, testInfo) => {
|
test('Can use the reset button to reset the image @unstable', async ({ page }, testInfo) => {
|
||||||
@ -211,47 +167,223 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await zoomIntoImageryByButton(page);
|
await zoomIntoImageryByButton(page);
|
||||||
await expect(pausePlayButton).not.toHaveClass(/is-paused/);
|
await expect(pausePlayButton).not.toHaveClass(/is-paused/);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// The following test case will cover these scenarios
|
test.describe('Example Imagery in Display Layout', () => {
|
||||||
// ('Can use Mouse Wheel to zoom in and out of previous image');
|
let displayLayout;
|
||||||
// ('Can use alt+drag to move around image once zoomed in');
|
test.beforeEach(async ({ page }) => {
|
||||||
// ('Clicking on the left arrow should pause the imagery and go to previous image');
|
// Go to baseURL
|
||||||
// ('If the imagery view is in pause mode, it should not be updated when new images come in');
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
// ('If the imagery view is not in pause mode, it should be updated when new images come in');
|
|
||||||
test('Example Imagery in Display layout @unstable', async ({ page }) => {
|
displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
||||||
test.info().annotations.push({
|
await page.goto(displayLayout.url);
|
||||||
type: 'issue',
|
|
||||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
/* Create Sine Wave Generator with minimum Image Load Delay */
|
||||||
|
// Click the Create button
|
||||||
|
await page.click('button:has-text("Create")');
|
||||||
|
|
||||||
|
// Click text=Example Imagery
|
||||||
|
await page.click('text=Example Imagery');
|
||||||
|
|
||||||
|
// Clear and set Image load delay to minimum value
|
||||||
|
await page.locator('input[type="number"]').fill('');
|
||||||
|
await page.locator('input[type="number"]').fill('5000');
|
||||||
|
|
||||||
|
// Click text=OK
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({waitUntil: 'networkidle'}),
|
||||||
|
page.click('text=OK'),
|
||||||
|
//Wait for Save Banner to appear
|
||||||
|
page.waitForSelector('.c-message-banner__message')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||||
|
|
||||||
|
await page.goto(displayLayout.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Go to baseURL
|
test('Imagery View operations @unstable', async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||||
|
});
|
||||||
|
|
||||||
// Click the Create button
|
// Edit mode
|
||||||
await page.click('button:has-text("Create")');
|
await page.click('button[title="Edit"]');
|
||||||
|
|
||||||
// Click text=Example Imagery
|
// Click on example imagery to expose toolbar
|
||||||
await page.click('text=Example Imagery');
|
await page.locator('.c-so-view__header').click();
|
||||||
|
|
||||||
// Clear and set Image load delay to minimum value
|
// Adjust object height
|
||||||
await page.locator('input[type="number"]').fill('');
|
await page.locator('div[title="Resize object height"] > input').click();
|
||||||
await page.locator('input[type="number"]').fill('5000');
|
await page.locator('div[title="Resize object height"] > input').fill('50');
|
||||||
|
|
||||||
// Click text=OK
|
// Adjust object width
|
||||||
await Promise.all([
|
await page.locator('div[title="Resize object width"] > input').click();
|
||||||
page.waitForNavigation({waitUntil: 'networkidle'}),
|
await page.locator('div[title="Resize object width"] > input').fill('50');
|
||||||
page.click('text=OK'),
|
|
||||||
//Wait for Save Banner to appear
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Wait until Save Banner is gone
|
await performImageryViewOperationsAndAssert(page);
|
||||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
});
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
|
|
||||||
|
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
|
||||||
|
const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
|
||||||
|
// Edit mode
|
||||||
|
await page.click('button[title="Edit"]');
|
||||||
|
|
||||||
|
// Click on example imagery to expose toolbar
|
||||||
|
await page.locator('.c-so-view__header').click();
|
||||||
|
|
||||||
|
// expect thumbnails not be visible when first added
|
||||||
|
expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy();
|
||||||
|
|
||||||
|
// Resize the example imagery vertically to change the thumbnail visibility
|
||||||
|
/*
|
||||||
|
The following arbitrary values are added to observe the separate visual
|
||||||
|
conditions of the thumbnails (hidden, small thumbnails, regular thumbnails).
|
||||||
|
Specifically, height is set to 50px for small thumbs and 100px for regular
|
||||||
|
*/
|
||||||
|
await page.locator('div[title="Resize object height"] > input').click();
|
||||||
|
await page.locator('div[title="Resize object height"] > input').fill('50');
|
||||||
|
|
||||||
|
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
|
||||||
|
await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/);
|
||||||
|
|
||||||
|
// Resize the example imagery vertically to change the thumbnail visibility
|
||||||
|
await page.locator('div[title="Resize object height"] > input').click();
|
||||||
|
await page.locator('div[title="Resize object height"] > input').fill('100');
|
||||||
|
|
||||||
|
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
|
||||||
|
await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Example Imagery in Flexible layout', () => {
|
||||||
|
let flexibleLayout;
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
||||||
|
await page.goto(flexibleLayout.url);
|
||||||
|
|
||||||
|
/* Create Sine Wave Generator with minimum Image Load Delay */
|
||||||
|
// Click the Create button
|
||||||
|
await page.click('button:has-text("Create")');
|
||||||
|
|
||||||
|
// Click text=Example Imagery
|
||||||
|
await page.click('text=Example Imagery');
|
||||||
|
|
||||||
|
// Clear and set Image load delay to minimum value
|
||||||
|
await page.locator('input[type="number"]').fill('');
|
||||||
|
await page.locator('input[type="number"]').fill('5000');
|
||||||
|
|
||||||
|
// Click text=OK
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({waitUntil: 'networkidle'}),
|
||||||
|
page.click('text=OK'),
|
||||||
|
//Wait for Save Banner to appear
|
||||||
|
page.waitForSelector('.c-message-banner__message')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||||
|
|
||||||
|
await page.goto(flexibleLayout.url);
|
||||||
|
});
|
||||||
|
test('Imagery View operations @unstable', async ({ page, browserName }) => {
|
||||||
|
test.fixme(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'
|
||||||
|
});
|
||||||
|
|
||||||
|
await performImageryViewOperationsAndAssert(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Example Imagery in Tabs View', () => {
|
||||||
|
let tabsView;
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
|
||||||
|
await page.goto(tabsView.url);
|
||||||
|
|
||||||
|
/* Create Sine Wave Generator with minimum Image Load Delay */
|
||||||
|
// Click the Create button
|
||||||
|
await page.click('button:has-text("Create")');
|
||||||
|
|
||||||
|
// Click text=Example Imagery
|
||||||
|
await page.click('text=Example Imagery');
|
||||||
|
|
||||||
|
// Clear and set Image load delay to minimum value
|
||||||
|
await page.locator('input[type="number"]').fill('');
|
||||||
|
await page.locator('input[type="number"]').fill('5000');
|
||||||
|
|
||||||
|
// Click text=OK
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({waitUntil: 'networkidle'}),
|
||||||
|
page.click('text=OK'),
|
||||||
|
//Wait for Save Banner to appear
|
||||||
|
page.waitForSelector('.c-message-banner__message')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||||
|
|
||||||
|
await page.goto(tabsView.url);
|
||||||
|
});
|
||||||
|
test('Imagery View operations @unstable', async ({ page }) => {
|
||||||
|
await performImageryViewOperationsAndAssert(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Example Imagery in Time Strip', () => {
|
||||||
|
let timeStripObject;
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
timeStripObject = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Time Strip',
|
||||||
|
name: 'Time Strip'.concat(' ', uuid())
|
||||||
|
});
|
||||||
|
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Example Imagery',
|
||||||
|
name: 'Example Imagery'.concat(' ', uuid()),
|
||||||
|
parent: timeStripObject.uuid
|
||||||
|
});
|
||||||
|
// Navigate to timestrip
|
||||||
|
await page.goto(timeStripObject.url);
|
||||||
|
});
|
||||||
|
test('Clicking a thumbnail loads the image in large view', async ({ page, browserName }) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/nasa/openmct/issues/5632'
|
||||||
|
});
|
||||||
|
await page.locator('.c-imagery-tsv-container').hover();
|
||||||
|
// get url of the hovered image
|
||||||
|
const hoveredImg = page.locator('.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img');
|
||||||
|
const hoveredImgSrc = await hoveredImg.getAttribute('src');
|
||||||
|
expect(hoveredImgSrc).toBeTruthy();
|
||||||
|
await page.locator('.c-imagery-tsv-container').click();
|
||||||
|
// get image of view large container
|
||||||
|
const viewLargeImg = page.locator('img.c-imagery__main-image__image');
|
||||||
|
const viewLargeImgSrc = await viewLargeImg.getAttribute('src');
|
||||||
|
expect(viewLargeImgSrc).toBeTruthy();
|
||||||
|
expect(viewLargeImgSrc).toEqual(hoveredImgSrc);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the common actions and assertions for the Imagery View.
|
||||||
|
* This function verifies the following in order:
|
||||||
|
* 1. Can zoom in/out using the zoom buttons
|
||||||
|
* 2. Can zoom in/out using the mouse wheel
|
||||||
|
* 3. Can pan the image using the pan hotkey + mouse drag
|
||||||
|
* 4. Clicking on the left arrow button pauses imagery and moves to the previous image
|
||||||
|
* 5. Imagery is updated as new images stream in, regardless of pause status
|
||||||
|
* 6. Old images are discarded when new images stream in
|
||||||
|
* 7. Image brightness/contrast can be adjusted by dragging the sliders
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
async function performImageryViewOperationsAndAssert(page) {
|
||||||
// Click previous image button
|
// Click previous image button
|
||||||
const previousImageButton = page.locator('.c-nav--prev');
|
const previousImageButton = page.locator('.c-nav--prev');
|
||||||
await previousImageButton.click();
|
await previousImageButton.click();
|
||||||
@ -260,27 +392,17 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => {
|
|||||||
const selectedImage = page.locator('.selected');
|
const selectedImage = page.locator('.selected');
|
||||||
await expect(selectedImage).toBeVisible();
|
await expect(selectedImage).toBeVisible();
|
||||||
|
|
||||||
// Zoom in
|
// Use the zoom buttons to zoom in and out
|
||||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
await buttonZoomOnImageAndAssert(page);
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
const deltaYStep = 100; // equivalent to 1x zoom
|
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
|
||||||
|
|
||||||
// Wait for zoom animation to finish
|
// Use Mouse Wheel to zoom in to previous image
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
await mouseZoomOnImageAndAssert(page, 2);
|
||||||
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
|
|
||||||
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
|
|
||||||
|
|
||||||
// Center the mouse pointer
|
// Use alt+drag to move around image once zoomed in
|
||||||
await page.mouse.move(imageCenterX, imageCenterY);
|
await panZoomAndAssertImageProperties(page);
|
||||||
|
|
||||||
// Pan Imagery Hints
|
// Use Mouse Wheel to zoom out of previous image
|
||||||
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
|
await mouseZoomOnImageAndAssert(page, -2);
|
||||||
expect(expectedAltText).toEqual(imageryHintsText);
|
|
||||||
|
|
||||||
// Click next image button
|
// Click next image button
|
||||||
const nextImageButton = page.locator('.c-nav--next');
|
const nextImageButton = page.locator('.c-nav--next');
|
||||||
@ -293,21 +415,14 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => {
|
|||||||
await page.locator('[data-testid=conductor-modeOption-realtime]').click();
|
await page.locator('[data-testid=conductor-modeOption-realtime]').click();
|
||||||
|
|
||||||
// Zoom in on next image
|
// Zoom in on next image
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
await mouseZoomOnImageAndAssert(page, 2);
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
|
||||||
|
|
||||||
// Wait for zoom animation to finish
|
// Clicking on the left arrow should pause the imagery and go to previous image
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
|
|
||||||
expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
|
|
||||||
|
|
||||||
// Click previous image button
|
|
||||||
await previousImageButton.click();
|
await previousImageButton.click();
|
||||||
|
await expect(page.locator('.c-button.pause-play')).toHaveClass(/is-paused/);
|
||||||
// Verify previous image
|
|
||||||
await expect(selectedImage).toBeVisible();
|
await expect(selectedImage).toBeVisible();
|
||||||
|
|
||||||
|
// The imagery view should be updated when new images come in
|
||||||
const imageCount = await page.locator('.c-imagery__thumb').count();
|
const imageCount = await page.locator('.c-imagery__thumb').count();
|
||||||
await expect.poll(async () => {
|
await expect.poll(async () => {
|
||||||
const newImageCount = await page.locator('.c-imagery__thumb').count();
|
const newImageCount = await page.locator('.c-imagery__thumb').count();
|
||||||
@ -315,7 +430,7 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => {
|
|||||||
return newImageCount;
|
return newImageCount;
|
||||||
}, {
|
}, {
|
||||||
message: "verify that old images are discarded",
|
message: "verify that old images are discarded",
|
||||||
timeout: 6 * 1000
|
timeout: 7 * 1000
|
||||||
}).toBe(imageCount);
|
}).toBe(imageCount);
|
||||||
|
|
||||||
// Verify selected image is still displayed
|
// Verify selected image is still displayed
|
||||||
@ -333,283 +448,6 @@ test('Example Imagery in Display layout @unstable', async ({ page }) => {
|
|||||||
// Drag the brightness and contrast sliders around and assert filter values
|
// Drag the brightness and contrast sliders around and assert filter values
|
||||||
await dragBrightnessSliderAndAssertFilterValues(page);
|
await dragBrightnessSliderAndAssertFilterValues(page);
|
||||||
await dragContrastSliderAndAssertFilterValues(page);
|
await dragContrastSliderAndAssertFilterValues(page);
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('Example imagery thumbnails resize in display layouts', () => {
|
|
||||||
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
|
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
|
|
||||||
// Click button:has-text("Create")
|
|
||||||
await page.locator('button:has-text("Create")').click();
|
|
||||||
|
|
||||||
// Click li:has-text("Display Layout")
|
|
||||||
await page.locator('li:has-text("Display Layout")').click();
|
|
||||||
const displayLayoutTitleField = page.locator('text=Properties Title Notes Horizontal grid (px) Vertical grid (px) Horizontal size ( >> input[type="text"]');
|
|
||||||
await displayLayoutTitleField.click();
|
|
||||||
|
|
||||||
await displayLayoutTitleField.fill('Thumbnail Display Layout');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('text=OK').click()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 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 button:has-text("Create")
|
|
||||||
await page.locator('button:has-text("Create")').click();
|
|
||||||
|
|
||||||
// Click li:has-text("Example Imagery")
|
|
||||||
await page.locator('li:has-text("Example Imagery")').click();
|
|
||||||
|
|
||||||
const imageryTitleField = page.locator('text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]');
|
|
||||||
// Click text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
|
|
||||||
await imageryTitleField.click();
|
|
||||||
|
|
||||||
// Fill text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
|
|
||||||
await imageryTitleField.fill('Thumbnail Example Imagery');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('text=OK').click()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Click text=Thumbnail Example Imagery Imagery Layout Snapshot >> button >> nth=0
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('text=Thumbnail Example Imagery Imagery Layout Snapshot >> button').first().click()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Edit mode
|
|
||||||
await page.locator('text=Thumbnail Display Layout Snapshot >> button').nth(3).click();
|
|
||||||
|
|
||||||
// Click on example imagery to expose toolbar
|
|
||||||
await page.locator('text=Thumbnail Example Imagery Snapshot Large View').click();
|
|
||||||
|
|
||||||
// expect thumbnails not be visible when first added
|
|
||||||
expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy();
|
|
||||||
|
|
||||||
// Resize the example imagery vertically to change the thumbnail visibility
|
|
||||||
/*
|
|
||||||
The following arbitrary values are added to observe the separate visual
|
|
||||||
conditions of the thumbnails (hidden, small thumbnails, regular thumbnails).
|
|
||||||
Specifically, height is set to 50px for small thumbs and 100px for regular
|
|
||||||
*/
|
|
||||||
// Click #mct-input-id-103
|
|
||||||
await page.locator('#mct-input-id-103').click();
|
|
||||||
|
|
||||||
// Fill #mct-input-id-103
|
|
||||||
await page.locator('#mct-input-id-103').fill('50');
|
|
||||||
|
|
||||||
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
|
|
||||||
await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/);
|
|
||||||
|
|
||||||
// Resize the example imagery vertically to change the thumbnail visibility
|
|
||||||
// Click #mct-input-id-103
|
|
||||||
await page.locator('#mct-input-id-103').click();
|
|
||||||
|
|
||||||
// Fill #mct-input-id-103
|
|
||||||
await page.locator('#mct-input-id-103').fill('100');
|
|
||||||
|
|
||||||
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
|
|
||||||
await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('Example Imagery in Flexible layout', () => {
|
|
||||||
test('Example Imagery in Flexible layout @unstable', async ({ page, browserName, openmctConfig }) => {
|
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
|
|
||||||
// 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'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Go to baseURL
|
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
// Click the Create button
|
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
// Click text=Example Imagery
|
|
||||||
await page.click('text=Example Imagery');
|
|
||||||
|
|
||||||
// Clear and set Image load delay (milliseconds)
|
|
||||||
await page.click('input[type="number"]', {clickCount: 3});
|
|
||||||
await page.type('input[type="number"]', "20");
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation({waitUntil: 'networkidle'}),
|
|
||||||
page.click('text=OK'),
|
|
||||||
//Wait for Save Banner to appear
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
// Wait until Save Banner is gone
|
|
||||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
|
||||||
|
|
||||||
// Click the Create button
|
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
// Click text=Flexible Layout
|
|
||||||
await page.click('text=Flexible Layout');
|
|
||||||
|
|
||||||
// Assert Flexible layout
|
|
||||||
await expect(page.locator('.js-form-title')).toHaveText('Create a New Flexible Layout');
|
|
||||||
|
|
||||||
await page.locator(`form[name="mctForm"] >> text=${myItemsFolderName}`).click();
|
|
||||||
|
|
||||||
// Click My Items
|
|
||||||
await Promise.all([
|
|
||||||
page.locator('text=OK').click(),
|
|
||||||
page.waitForNavigation({waitUntil: 'networkidle'})
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Click My Items
|
|
||||||
await page.locator('.c-disclosure-triangle').click();
|
|
||||||
|
|
||||||
// Right click example imagery
|
|
||||||
await page.click(('text=Unnamed Example Imagery'), { button: 'right' });
|
|
||||||
|
|
||||||
// Click move
|
|
||||||
await page.locator('.icon-move').click();
|
|
||||||
|
|
||||||
// Click triangle to open sub menu
|
|
||||||
await page.locator('.c-form__section .c-disclosure-triangle').click();
|
|
||||||
|
|
||||||
// Click Flexable Layout
|
|
||||||
await page.click('.c-overlay__outer >> text=Unnamed Flexible Layout');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await page.locator('text=OK').click();
|
|
||||||
|
|
||||||
// Save template
|
|
||||||
await saveTemplate(page);
|
|
||||||
|
|
||||||
// Zoom in
|
|
||||||
await mouseZoomIn(page);
|
|
||||||
|
|
||||||
// Center the mouse pointer
|
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
|
||||||
await page.mouse.move(imageCenterX, imageCenterY);
|
|
||||||
|
|
||||||
// Pan zoom
|
|
||||||
await panZoomAndAssertImageProperties(page);
|
|
||||||
|
|
||||||
// Click previous image button
|
|
||||||
const previousImageButton = page.locator('.c-nav--prev');
|
|
||||||
await previousImageButton.click();
|
|
||||||
|
|
||||||
// Verify previous image
|
|
||||||
const selectedImage = page.locator('.selected');
|
|
||||||
await expect(selectedImage).toBeVisible();
|
|
||||||
|
|
||||||
// Click time conductor mode button
|
|
||||||
await page.locator('.c-mode-button').click();
|
|
||||||
|
|
||||||
// Select local clock mode
|
|
||||||
await page.locator('[data-testid=conductor-modeOption-realtime]').nth(0).click();
|
|
||||||
|
|
||||||
// Zoom in on next image
|
|
||||||
await mouseZoomIn(page);
|
|
||||||
|
|
||||||
// Click previous image button
|
|
||||||
await previousImageButton.click();
|
|
||||||
|
|
||||||
// Verify previous image
|
|
||||||
await expect(selectedImage).toBeVisible();
|
|
||||||
|
|
||||||
const imageCount = await page.locator('.c-imagery__thumb').count();
|
|
||||||
await expect.poll(async () => {
|
|
||||||
const newImageCount = await page.locator('.c-imagery__thumb').count();
|
|
||||||
|
|
||||||
return newImageCount;
|
|
||||||
}, {
|
|
||||||
message: "verify that old images are discarded",
|
|
||||||
timeout: 6 * 1000
|
|
||||||
}).toBe(imageCount);
|
|
||||||
|
|
||||||
// Verify selected image is still displayed
|
|
||||||
await expect(selectedImage).toBeVisible();
|
|
||||||
|
|
||||||
// Unpause imagery
|
|
||||||
await page.locator('.pause-play').click();
|
|
||||||
|
|
||||||
//Get background-image url from background-image css prop
|
|
||||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
|
||||||
|
|
||||||
// Open the image filter menu
|
|
||||||
await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click();
|
|
||||||
|
|
||||||
// Drag the brightness and contrast sliders around and assert filter values
|
|
||||||
await dragBrightnessSliderAndAssertFilterValues(page);
|
|
||||||
await dragContrastSliderAndAssertFilterValues(page);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('Example Imagery in Tabs view', () => {
|
|
||||||
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('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
|
|
||||||
test.fixme('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
|
|
||||||
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, it should not be updated when new images 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 Time Strip', () => {
|
|
||||||
test('ensure that clicking a thumbnail loads the image in large view', async ({ page, browserName }) => {
|
|
||||||
test.info().annotations.push({
|
|
||||||
type: 'issue',
|
|
||||||
description: 'https://github.com/nasa/openmct/issues/5632'
|
|
||||||
});
|
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
|
||||||
const timeStripObject = await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Time Strip',
|
|
||||||
name: 'Time Strip'.concat(' ', uuid())
|
|
||||||
});
|
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Example Imagery',
|
|
||||||
name: 'Example Imagery'.concat(' ', uuid()),
|
|
||||||
parent: timeStripObject.uuid
|
|
||||||
});
|
|
||||||
// Navigate to timestrip
|
|
||||||
await page.goto(timeStripObject.url);
|
|
||||||
|
|
||||||
await page.locator('.c-imagery-tsv-container').hover();
|
|
||||||
// get url of the hovered image
|
|
||||||
const hoveredImg = page.locator('.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img');
|
|
||||||
const hoveredImgSrc = await hoveredImg.getAttribute('src');
|
|
||||||
expect(hoveredImgSrc).toBeTruthy();
|
|
||||||
await page.locator('.c-imagery-tsv-container').click();
|
|
||||||
// get image of view large container
|
|
||||||
const viewLargeImg = page.locator('img.c-imagery__main-image__image');
|
|
||||||
const viewLargeImgSrc = await viewLargeImg.getAttribute('src');
|
|
||||||
expect(viewLargeImgSrc).toBeTruthy();
|
|
||||||
expect(viewLargeImgSrc).toEqual(hoveredImgSrc);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
async function saveTemplate(page) {
|
|
||||||
await page.locator('.c-button--menu.c-button--major.icon-save').click();
|
|
||||||
await page.locator('text=Save and Finish Editing').click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -692,7 +530,7 @@ async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
|||||||
return backgroundImageUrl2;
|
return backgroundImageUrl2;
|
||||||
}, {
|
}, {
|
||||||
message: "verify next image has updated",
|
message: "verify next image has updated",
|
||||||
timeout: 6 * 1000
|
timeout: 7 * 1000
|
||||||
}).not.toBe(backgroundImageUrl1);
|
}).not.toBe(backgroundImageUrl1);
|
||||||
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
|
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
|
||||||
}
|
}
|
||||||
@ -746,14 +584,17 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Use the mouse wheel to zoom in or out of an image and assert that the image
|
||||||
|
* has successfully zoomed in or out.
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {number} [factor = 2] The zoom factor. Positive for zoom in, negative for zoom out.
|
||||||
*/
|
*/
|
||||||
async function mouseZoomIn(page) {
|
async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
||||||
// Zoom in
|
// Zoom in
|
||||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
await page.locator(backgroundImageSelector).hover({trial: true});
|
||||||
const deltaYStep = 100; // equivalent to 1x zoom
|
const deltaYStep = 100; // equivalent to 1x zoom
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
await page.mouse.wheel(0, deltaYStep * factor);
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
@ -763,9 +604,47 @@ async function mouseZoomIn(page) {
|
|||||||
|
|
||||||
// Wait for zoom animation to finish
|
// Wait for zoom animation to finish
|
||||||
await page.locator(backgroundImageSelector).hover({trial: true});
|
await page.locator(backgroundImageSelector).hover({trial: true});
|
||||||
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
|
|
||||||
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
|
if (factor > 0) {
|
||||||
|
expect(imageMouseZoomed.height).toBeGreaterThan(originalImageDimensions.height);
|
||||||
|
expect(imageMouseZoomed.width).toBeGreaterThan(originalImageDimensions.width);
|
||||||
|
} else {
|
||||||
|
expect(imageMouseZoomed.height).toBeLessThan(originalImageDimensions.height);
|
||||||
|
expect(imageMouseZoomed.width).toBeLessThan(originalImageDimensions.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom in and out of the image using the buttons, and assert that the image has
|
||||||
|
* been successfully zoomed in or out.
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
async function buttonZoomOnImageAndAssert(page) {
|
||||||
|
// Get initial image dimensions
|
||||||
|
const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
|
|
||||||
|
// Zoom in twice via button
|
||||||
|
await zoomIntoImageryByButton(page);
|
||||||
|
await zoomIntoImageryByButton(page);
|
||||||
|
|
||||||
|
// Get and assert zoomed in image dimensions
|
||||||
|
const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
|
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||||
|
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||||
|
|
||||||
|
// Zoom out once via button
|
||||||
|
await zoomOutOfImageryByButton(page);
|
||||||
|
|
||||||
|
// Get and assert zoomed out image dimensions
|
||||||
|
const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
|
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||||
|
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||||
|
|
||||||
|
// Zoom out again via button, assert against the initial image dimensions
|
||||||
|
await zoomOutOfImageryByButton(page);
|
||||||
|
const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
|
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"@braintree/sanitize-url": "6.0.0",
|
"@braintree/sanitize-url": "6.0.0",
|
||||||
"@percy/cli": "1.7.2",
|
"@percy/cli": "1.7.2",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.25.0",
|
"@playwright/test": "1.25.2",
|
||||||
"@types/eventemitter3": "^1.0.0",
|
"@types/eventemitter3": "^1.0.0",
|
||||||
"@types/jasmine": "^4.0.1",
|
"@types/jasmine": "^4.0.1",
|
||||||
"@types/karma": "^6.3.2",
|
"@types/karma": "^6.3.2",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user