Skip to content

Commit

Permalink
fix(#7552): Fix notebook snapshot image annotations (#7555)
Browse files Browse the repository at this point in the history
* fix: painterro import

* test(snapshotAnnotation): add minimal e2e test

* chore: add e2e test annotation

* fix: notebook snapshot test

* refactor: put `v-else` on template

* small changes to the test and a visual one

* additional a11y

* fix: html structure

* test(e2e): fix notebook snapshot tests

* Update documentation for file download and JSON testing

* Update stubs and add jpg/png export

* refactor(TimelistComponent): tidy up

---------

Co-authored-by: John Hill <john.c.hill@nasa.gov>
  • Loading branch information
ozyx and unlikelyzero authored Mar 13, 2024
1 parent 18e976a commit faed27c
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 117 deletions.
24 changes: 24 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,30 @@ test.describe('foo test suite', () => {
- Working with multiple pages
There are instances where multiple browser pages will needed to verify multi-page or multi-tab application behavior. Make sure to use the `@2p` annotation as well as name each page appropriately: i.e. `page1` and `page2` or `tab1` and `tab2` depending on the intended use case. Generally pages should be used unless testing `sharedWorker` code, specifically.
- Working with file downloads and JSON data
Open MCT has the capability of exporting certain objects in the form of a JSON file handled by the chrome browser. The best example of this type of test can be found in the exportAsJson test.
```js
const [download] = await Promise.all([
page.waitForEvent('download'), // Waits for the download event
page.getByLabel('Export as JSON').click() // Triggers the download
]);

// Wait for the download process to complete
const path = await download.path();

// Read the contents of the downloaded file using readFile from fs/promises
const fileContents = await fs.readFile(path, 'utf8');
const jsonData = JSON.parse(fileContents);

// Use the function to retrieve the key
const key = getFirstKeyFromOpenMctJson(jsonData);

// Verify the contents of the JSON file
expect(jsonData.openmct[key]).toHaveProperty('name', 'e2e folder');
```
### Reporting
Test Reporting is done through official Playwright reporters and the CI Systems which execute them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
await page.getByTitle('Add Container').click();
expect(await containerHandles.count()).toEqual(3);
await page.getByTitle('Remove Container').click();
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
);
await page.getByRole('button', { name: 'OK', exact: true }).click();
Expand All @@ -299,7 +299,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(2);
await page.getByRole('group', { name: 'Child Layout 1' }).click();
await page.getByTitle('Remove Frame').click();
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
'This action will remove this frame from this Flexible Layout. Do you want to continue?'
);
await page.getByRole('button', { name: 'OK', exact: true }).click();
Expand Down
116 changes: 76 additions & 40 deletions e2e/tests/functional/plugins/notebook/notebookSnapshots.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,42 +71,89 @@ test.describe('Snapshot Container tests', () => {
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });

// Create Notebook
// const notebook = await createDomainObjectWithDefaults(page, {
// type: 'Notebook',
// name: "Test Notebook"
// });
// // Create Overlay Plot
// const snapShotObject = await createDomainObjectWithDefaults(page, {
// type: 'Overlay Plot',
// name: "Dropped Overlay Plot"
// });

await page.getByLabel('Open the Notebook Snapshot Menu').click();
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click();
await page.getByLabel('Show Snapshots').click();
});
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByLabel('My Items Notebook Embed').getByLabel('More actions').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
await expect(page.getByLabel('Modal Overlay')).toBeVisible();
await expect(page.getByLabel('Preview Container')).toBeVisible();
});
test('A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/7552'
});
//Open Snapshot Object View
await page.getByLabel('My Items Notebook Embed').getByLabel('More actions').click();
await page.getByRole('menuitem', { name: 'View Snapshot' }).click();
await expect(page.getByRole('dialog', { name: 'Modal Overlay' })).toBeVisible();
await expect(page.locator('#snapshotDescriptor')).toHaveText(
/SNAPSHOT \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/
);
// Open Annotation Editor with Painterro
await page.getByLabel('Annotate this snapshot').click();
await expect(page.locator('#snap-annotation-canvas')).toBeVisible();
// Clear the canvas
await page.getByRole('button', { name: 'Put text [T]' }).click();
// Click in the Painterro canvas to add a text annotation
await page.locator('.ptro-crp-el').click();
await page.locator('.ptro-text-tool-input').fill('...is there life on mars?');
// When working with Painterro, we need to check that the Apply button is hidden after clicking
await page.getByTitle('Apply').click();
await expect(page.getByTitle('Apply')).toBeHidden();

// Save and exit annotation window
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();

// Open up annotation again
await page.getByRole('img', { name: 'My Items thumbnail' }).click();
await expect(page.getByLabel('Modal Overlay').getByRole('img')).toBeVisible();
});
test('A snapshot can be Annotated and saved as a JPG and PNG', async ({ page }) => {
//Open Snapshot Object View
await page.getByLabel('My Items Notebook Embed').getByLabel('More actions').click();
await page.getByRole('menuitem', { name: 'View Snapshot' }).click();
await expect(page.getByRole('dialog', { name: 'Modal Overlay' })).toBeVisible();

// Open Annotation Editor with Painterro
await page.getByLabel('Annotate this snapshot').click();
await expect(page.locator('#snap-annotation-canvas')).toBeVisible();
// Clear the canvas
await page.getByRole('button', { name: 'Put text [T]' }).click();
// Click in the Painterro canvas to add a text annotation
await page.locator('.ptro-crp-el').click();
await page.locator('.ptro-text-tool-input').fill('...is there life on mars?');
// When working with Painterro, we need to check that the Apply button is hidden after clicking
await page.getByTitle('Apply').click();
await expect(page.getByTitle('Apply')).toBeHidden();

// Save and exit annotation window
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();

// Open up annotation again
await page.getByRole('img', { name: 'My Items thumbnail' }).click();
await expect(page.getByLabel('Modal Overlay').getByRole('img')).toBeVisible();

// Save as JPG
await Promise.all([
page.waitForEvent('download'), // Waits for the download event
page.getByLabel('Export as JPG').click() // Triggers the download
]);

// Save as PNG
await expect(page.getByLabel('Modal Overlay').getByRole('img')).toBeVisible();
await Promise.all([
page.waitForEvent('download'), // Waits for the download event
page.getByLabel('Export as PNG').click() // Triggers the download
]);
});
test.fixme(
'A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu',
async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: ' View Snapshot' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
await page.getByTitle('Annotate').click();
await expect(page.locator('#snap-annotation-canvas')).toBeVisible();
await page.getByRole('button', { name: '' }).click();
// await expect(page.locator('#snap-annotation-canvas')).not.toBeVisible();
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();
//await expect(await page.locator)
}
);
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme(
'5 Snapshots can be added to a container and Deleted with Delete All action',
Expand All @@ -116,10 +163,6 @@ test.describe('Snapshot Container tests', () => {
'A snapshot can be Deleted from Container with 3 dot action menu',
async ({ page }) => {}
);
test.fixme(
'A snapshot can be Navigated To from Container with 3 dot action menu',
async ({ page }) => {}
);
test.fixme(
'A snapshot can be Navigated To Item in Time from Container with 3 dot action menu',
async ({ page }) => {}
Expand Down Expand Up @@ -151,11 +194,4 @@ test.describe('Snapshot Container tests', () => {
//Snapshot removed from container?
}
);
test.fixme(
'Verify Embedded options for PNG, JPG, and Annotate work correctly',
async ({ page }) => {
//Add snapshot to container
//Verify PNG, JPG, and Annotate buttons work correctly
}
);
});
40 changes: 39 additions & 1 deletion e2e/tests/visual-a11y/notebook.visual.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import percySnapshot from '@percy/playwright';

import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../appActions.js';
import { test } from '../../avpFixtures.js';
import { expect, test } from '../../avpFixtures.js';
import { VISUAL_URL } from '../../constants.js';
import { enterTextEntry, startAndAddRestrictedNotebookObject } from '../../helper/notebookUtils.js';

Expand All @@ -39,6 +39,44 @@ test.describe('Visual - Restricted Notebook @a11y', () => {
});
});

test.describe('Visual - Notebook Snapshot @a11y', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./?hideTree=true&hideInspector=true', { waitUntil: 'domcontentloaded' });
});
test('Visual check for Snapshot Annotation', async ({ page, theme }) => {
await page.getByLabel('Open the Notebook Snapshot Menu').click();
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click();
await page.getByLabel('Show Snapshots').click();

await page.getByLabel('My Items Notebook Embed').getByLabel('More actions').click();
await page.getByRole('menuitem', { name: 'View Snapshot' }).click();

await page.getByLabel('Annotate this snapshot').click();
await expect(page.locator('#snap-annotation-canvas')).toBeVisible();
// Clear the canvas
await page.getByRole('button', { name: 'Put text [T]' }).click();
// Click in the Painterro canvas to add a text annotation
await page.locator('.ptro-crp-el').click();
await page.locator('.ptro-text-tool-input').fill('...is there life on mars?');
await percySnapshot(page, `Notebook Snapshot with text entry open (theme: '${theme}')`);

// When working with Painterro, we need to check that the Apply button is hidden after clicking
await page.getByTitle('Apply').click();
await expect(page.getByTitle('Apply')).toBeHidden();

// Save and exit annotation window
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();

// Open up annotation again
await page.getByRole('img', { name: 'My Items thumbnail' }).click();
await expect(page.getByLabel('Modal Overlay').getByRole('img')).toBeVisible();

// Take a snapshot
await percySnapshot(page, `Notebook Snapshot with annotation (theme: '${theme}')`);
});
});

test.describe('Visual - Notebook @a11y', () => {
let notebook;
test.beforeEach(async ({ page }) => {
Expand Down
7 changes: 2 additions & 5 deletions src/api/overlays/components/OverlayComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<template>
<div class="c-overlay js-overlay">
<div class="c-overlay js-overlay" role="dialog" aria-modal="true" aria-label="Modal Overlay">
<div class="c-overlay__blocker" @click="destroy"></div>
<div class="c-overlay__outer">
<button
Expand All @@ -34,9 +34,6 @@
ref="element"
class="c-overlay__contents js-notebook-snapshot-item-wrapper"
tabindex="0"
aria-modal="true"
aria-label="Overlay"
role="dialog"
></div>
<div v-if="buttons" class="c-overlay__button-bar">
<button
Expand All @@ -61,7 +58,7 @@
export default {
inject: ['dismiss', 'element', 'buttons', 'dismissable'],
emits: ['destroy'],
data: function () {
data() {
return {
focusIndex: -1
};
Expand Down
1 change: 1 addition & 0 deletions src/plugins/notebook/components/NotebookEmbed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<div
ref="notebookEmbed"
class="c-snapshot c-ne__embed"
:aria-label="`${embed.name} Notebook Embed`"
@mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip"
>
Expand Down
36 changes: 17 additions & 19 deletions src/plugins/notebook/components/snapshot-template.html
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
<div class="c-notebook-snapshot">
<!-- parent container sets up this for flex column layout -->
<div class="c-notebook-snapshot__header l-browse-bar">
<div class="l-browse-bar__start">
<div class="l-browse-bar__object-name--w">
<span class="c-object-label l-browse-bar__object-name">
<span class="c-object-label__type-icon" v-bind:class="cssClass"></span>
<span class="c-object-label__type-icon" :class="cssClass"></span>
<span class="c-object-label__name">{{ name }}</span>
</span>
</div>
</div>

<div id="snapshotDescriptor" class="l-browse-bar__snapshot-datetime">
SNAPSHOT {{ createdOn }}
</div>
<div class="c-button-set c-button-set--strip-h" role="toolbar">
<button class="c-button icon-download" aria-label="Export as PNG" @click="exportImage('png')">
<span class="c-button__label">PNG</span>
</button>
<button class="c-button icon-download" aria-label="Export as JPG" @click="exportImage('jpg')">
<span class="c-button__label">JPG</span>
</button>
</div>
<div class="l-browse-bar__end">
<div class="l-browse-bar__snapshot-datetime">SNAPSHOT {{ createdOn }}</div>
<span class="c-button-set c-button-set--strip-h">
<button
class="c-button icon-download"
title="Export This View's Data as PNG"
@click="exportImage('png')"
>
<span class="c-button__label">PNG</span>
</button>
<button class="c-button" title="Export This View's Data as JPG" @click="exportImage('jpg')">
<span class="c-button__label">JPG</span>
</button>
</span>
<a
<button
class="l-browse-bar__annotate-button c-button icon-pencil"
title="Annotate"
aria-label="Annotate this snapshot"
@click="annotateSnapshot"
>
<span class="title-label">Annotate</span>
</a>
</button>
</div>
</div>

<div
ref="snapshot-image"
class="c-notebook-snapshot__image"
:style="{ backgroundImage: 'url(' + src + ')' }"
role="img"
alt="Annotatable Snapshot"
></div>
</div>
2 changes: 1 addition & 1 deletion src/plugins/notebook/utils/painterroInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default class PainterroInstance {
this.config.id = this.elementId;
this.config.saveHandler = this.saveHandler.bind(this);

this.painterro = Painterro(this.config);
this.painterro = Painterro.default(this.config);
}

save(callback) {
Expand Down
Loading

0 comments on commit faed27c

Please sign in to comment.