Skip to content

Commit

Permalink
When dropping an unsupported file onto a notebook entry, tell the use…
Browse files Browse the repository at this point in the history
…r it isnt supported (#7115)

* handle unknown files and deal with copy/paste

* add some nice errors if couch pukes

* added how to adjust couchdb limits

* throw error on anntotation change

* keep blockquotes

* add test for blockquotes

* allow multi-file drop of images too

* add test for big files

* spell check

* check for null

* need to ignore console errors

* reorder tests so we can ignore console errors

* when creating new entry, ready it for editing

* fix tests and empty embeds

* fix tests

* found similar issue from notebooks in plots
  • Loading branch information
scottbell committed Oct 12, 2023
1 parent a16a1d3 commit 26d3bd1
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 72 deletions.
6 changes: 5 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,11 @@
"websockets",
"swgs",
"memlab",
"devmode"
"devmode",
"blockquote",
"blockquotes",
"Blockquote",
"Blockquotes"
],
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
"ignorePaths": [
Expand Down
2 changes: 1 addition & 1 deletion e2e/helper/notebookUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ async function enterTextEntry(page, text) {
await page.locator(NOTEBOOK_DROP_AREA).click();

// enter text
await page.getByLabel('Notebook Entry Display').last().click();
await page.getByLabel('Notebook Entry Input').last().fill(text);
await commitEntry(page);
}
Expand All @@ -53,6 +52,7 @@ async function dragAndDropEmbed(page, notebookObject) {
await page.click('button[title="Show selected item in tree"]');
// Drag and drop the SWG into the notebook
await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA);
await commitEntry(page);
}

/**
Expand Down
21 changes: 17 additions & 4 deletions e2e/tests/functional/plugins/notebook/notebook.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ test.describe('Notebook entry tests', () => {

// Click .c-notebook__drag-area
await page.locator('.c-notebook__drag-area').click();
await expect(page.getByLabel('Notebook Entry Display')).toBeVisible();
await expect(page.getByLabel('Notebook Entry Input')).toBeVisible();
await expect(page.getByLabel('Notebook Entry', { exact: true })).toHaveClass(/is-selected/);
});
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({
Expand Down Expand Up @@ -514,10 +514,23 @@ test.describe('Notebook entry tests', () => {
const childItem = page.locator('li:has-text("List Item 2") ol li:has-text("Order 2")');
await expect(childItem).toBeVisible();

// Blocks
const blockTest = '```javascript\nconst foo = "bar";\nconst bar = "foo";\n```';
await nbUtils.enterTextEntry(page, blockTest);
// Code Blocks
const codeblockTest = '```javascript\nconst foo = "bar";\nconst bar = "foo";\n```';
await nbUtils.enterTextEntry(page, codeblockTest);
const codeBlock = page.locator('code.language-javascript:has-text("const foo = \\"bar\\";")');
await expect(codeBlock).toBeVisible();

// Blockquotes
const blockquoteTest =
'This is a quote by Mark Twain:\n> "The man with a new idea is a crank\n>until the idea succeeds."';
await nbUtils.enterTextEntry(page, blockquoteTest);
const firstLineOfBlockquoteText = page.locator(
'blockquote:has-text("The man with a new idea is a crank")'
);
await expect(firstLineOfBlockquoteText).toBeVisible();
const secondLineOfBlockquoteText = page.locator(
'blockquote:has-text("until the idea succeeds")'
);
await expect(secondLineOfBlockquoteText).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,11 @@ test.describe('Snapshot image tests', () => {
}, fileData);

await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer });

await page.locator('.c-ne__save-button > button').click();
// be sure that entry was created
await expect(page.getByText('favicon-96x96.png')).toBeVisible();

await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).click();

// expect large image to be displayed
await expect(page.getByRole('dialog').getByText('favicon-96x96.png')).toBeVisible();

Expand All @@ -215,3 +214,59 @@ test.describe('Snapshot image tests', () => {
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1);
});
});

test.describe('Snapshot image failure tests', () => {
test.use({ failOnConsoleError: false });
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });

// Create Notebook
await createDomainObjectWithDefaults(page, {
type: NOTEBOOK_NAME
});
});

test('Get an error notification when dropping unknown file onto notebook entry', async ({
page
}) => {
// fill Uint8Array array with some garbage data
const garbageData = new Uint8Array(100);
const fileData = Array.from(garbageData);

const dropTransfer = await page.evaluateHandle((data) => {
const dataTransfer = new DataTransfer();
const file = new File([new Uint8Array(data)], 'someGarbage.foo', { type: 'unknown/garbage' });
dataTransfer.items.add(file);
return dataTransfer;
}, fileData);

await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer });

// should have gotten a notification from OpenMCT that we couldn't add it
await expect(page.getByText('Unknown object(s) dropped and cannot embed')).toBeVisible();
});

test('Get an error notification when dropping big files onto notebook entry', async ({
page
}) => {
const garbageSize = 15 * 1024 * 1024; // 15 megabytes

await page.addScriptTag({
// make the garbage client side
content: `window.bigGarbageData = new Uint8Array(${garbageSize})`
});

const bigDropTransfer = await page.evaluateHandle(() => {
const dataTransfer = new DataTransfer();
const file = new File([window.bigGarbageData], 'bigBoy.png', { type: 'image/png' });
dataTransfer.items.add(file);
return dataTransfer;
});

await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: bigDropTransfer });

// should have gotten a notification from OpenMCT that we couldn't add it as it's too big
await expect(page.getByText('unable to embed')).toBeVisible();
});
});
1 change: 0 additions & 1 deletion e2e/tests/functional/plugins/notebook/tags.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ test.describe('Tagging in Notebooks @addInit', () => {
await createNotebookEntryAndTags(page);

await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.getByLabel('Notebook Entry Display').last().click();
await page.getByLabel('Notebook Entry Input').fill(`An entry without tags`);
await page.locator('.c-ne__save-button > button').click();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ test.describe('Performance tests', () => {
await page.evaluate(() => window.performance.mark('new-notebook-entry-created'));

// Enter Notebook Entry text
await page.getByLabel('Notebook Entry').last().click();
await page.getByLabel('Notebook Entry Input').last().fill('New Entry');
await page.locator('.c-ne__save-button').click();
await page.evaluate(() => window.performance.mark('new-notebook-entry-filled'));
Expand Down
55 changes: 40 additions & 15 deletions src/plugins/notebook/components/NotebookComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -625,29 +625,43 @@ export default {
dropEvent.preventDefault();
dropEvent.stopImmediatePropagation();
const localImageDropped = dropEvent.dataTransfer.files?.[0]?.type.includes('image');
const imageUrl = dropEvent.dataTransfer.getData('URL');
const dataTransferFiles = Array.from(dropEvent.dataTransfer.files);
const localImageDropped = dataTransferFiles.some((file) => file.type.includes('image'));
const snapshotId = dropEvent.dataTransfer.getData('openmct/snapshot/id');
const domainObjectData = dropEvent.dataTransfer.getData('openmct/domain-object-path');
const imageUrl = dropEvent.dataTransfer.getData('URL');
if (localImageDropped) {
// local image dropped from disk (file)
const imageData = dropEvent.dataTransfer.files[0];
const imageEmbed = await createNewImageEmbed(imageData, this.openmct, imageData?.name);
this.newEntry(imageEmbed);
// local image(s) dropped from disk (file)
const embeds = [];
await Promise.all(
dataTransferFiles.map(async (file) => {
if (file.type.includes('image')) {
const imageData = file;
const imageEmbed = await createNewImageEmbed(
imageData,
this.openmct,
imageData?.name
);
embeds.push(imageEmbed);
}
})
);
this.newEntry(embeds);
} else if (imageUrl) {
// remote image dropped (URL)
try {
const response = await fetch(imageUrl);
const imageData = await response.blob();
const imageEmbed = await createNewImageEmbed(imageData, this.openmct);
this.newEntry(imageEmbed);
this.newEntry([imageEmbed]);
} catch (error) {
this.openmct.notifications.alert(`Unable to add image: ${error.message} `);
console.error(`Problem embedding remote image`, error);
}
} else if (snapshotId.length) {
// snapshot object
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
this.newEntry(snapshot.embedObject);
this.newEntry([snapshot.embedObject]);
this.snapshotContainer.removeSnapshot(snapshotId);
const namespace = this.domainObject.identifier.namespace;
Expand All @@ -656,10 +670,9 @@ export default {
namespace
);
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
} else {
} else if (domainObjectData) {
// plain domain object
const data = dropEvent.dataTransfer.getData('openmct/domain-object-path');
const objectPath = JSON.parse(data);
const objectPath = JSON.parse(domainObjectData);
const bounds = this.openmct.time.bounds();
const snapshotMeta = {
bounds,
Expand All @@ -668,8 +681,15 @@ export default {
openmct: this.openmct
};
const embed = await createNewEmbed(snapshotMeta);
this.newEntry(embed);
this.newEntry([embed]);
} else {
this.openmct.notifications.error(
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
);
console.warn(
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
);
return;
}
},
focusOnEntryId() {
Expand Down Expand Up @@ -838,12 +858,12 @@ export default {
getSelectedSectionId() {
return this.selectedSection?.id;
},
async newEntry(embed, event) {
async newEntry(embeds, event) {
this.startTransaction();
this.resetSearch();
const notebookStorage = this.createNotebookStorageObject();
this.updateDefaultNotebook(notebookStorage);
const id = await addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed);
const id = await addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embeds);
const element = this.$refs.notebookEntries.querySelector(`#${id}`);
const entryAnnotations = this.notebookAnnotations[id] ?? {};
Expand All @@ -861,6 +881,11 @@ export default {
this.filterAndSortEntries();
this.focusEntryId = id;
this.selectedEntryId = id;
// put entry into edit mode
this.$nextTick(() => {
element.dispatchEvent(new Event('click'));
});
},
orientationChange() {
this.formatSidebar();
Expand Down

0 comments on commit 26d3bd1

Please sign in to comment.