2024-08-02 15:47:43 +02:00
|
|
|
import { test, expect } from './util/test';
|
|
|
|
import MainScreen from './models/MainScreen';
|
|
|
|
import { join } from 'path';
|
|
|
|
import getImageSourceSize from './util/getImageSourceSize';
|
2024-08-17 13:22:03 +02:00
|
|
|
import setFilePickerResponse from './util/setFilePickerResponse';
|
2024-09-12 18:54:10 +02:00
|
|
|
import activateMainMenuItem from './util/activateMainMenuItem';
|
2024-08-02 15:47:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
test.describe('markdownEditor', () => {
|
|
|
|
test('preview pane should render images in HTML notes', async ({ mainWindow, electronApp }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.waitFor();
|
|
|
|
|
|
|
|
await mainScreen.importHtmlDirectory(electronApp, join(__dirname, 'resources', 'html-import'));
|
|
|
|
const importedFolder = mainScreen.sidebar.container.getByText('html-import');
|
|
|
|
await importedFolder.waitFor();
|
|
|
|
|
2024-09-04 13:14:12 +02:00
|
|
|
// Retry -- focusing the imported-folder may fail in some cases
|
|
|
|
await expect(async () => {
|
|
|
|
await importedFolder.click();
|
|
|
|
|
|
|
|
await mainScreen.noteList.focusContent(electronApp);
|
|
|
|
|
|
|
|
const importedHtmlFileItem = mainScreen.noteList.getNoteItemByTitle('test-html-file-with-image');
|
|
|
|
await importedHtmlFileItem.click({ timeout: 300 });
|
|
|
|
}).toPass();
|
2024-08-02 15:47:43 +02:00
|
|
|
|
2024-09-12 18:54:10 +02:00
|
|
|
const viewerFrame = mainScreen.noteEditor.getNoteViewerFrameLocator();
|
2024-08-02 15:47:43 +02:00
|
|
|
// Should render headers
|
|
|
|
await expect(viewerFrame.locator('h1')).toHaveText('Test HTML file!');
|
|
|
|
|
|
|
|
// Should render images
|
|
|
|
const image = viewerFrame.getByAltText('An SVG image.');
|
|
|
|
await expect(image).toBeAttached();
|
|
|
|
await expect(await getImageSourceSize(image)).toMatchObject([117, 30]);
|
|
|
|
});
|
2024-08-03 17:42:46 +02:00
|
|
|
|
2024-08-17 13:22:03 +02:00
|
|
|
test('preview pane should render PDFs', async ({ mainWindow, electronApp }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.createNewNote('PDF attachments');
|
|
|
|
const editor = mainScreen.noteEditor;
|
|
|
|
|
|
|
|
await editor.focusCodeMirrorEditor();
|
|
|
|
|
|
|
|
await setFilePickerResponse(electronApp, [join(__dirname, 'resources', 'small-pdf.pdf')]);
|
|
|
|
await editor.attachFileButton.click();
|
|
|
|
|
2024-09-12 18:54:10 +02:00
|
|
|
const viewerFrame = mainScreen.noteEditor.getNoteViewerFrameLocator();
|
2024-08-17 13:22:03 +02:00
|
|
|
const pdfLink = viewerFrame.getByText('small-pdf.pdf');
|
|
|
|
await expect(pdfLink).toBeVisible();
|
|
|
|
|
|
|
|
const expectToBeRendered = async () => {
|
|
|
|
|
|
|
|
// PDF preview should render
|
|
|
|
const pdfViewer = viewerFrame.locator('object[data$=".pdf"]');
|
|
|
|
// Should create the PDF viewer. Note: This is not sufficient to determine that the PDF viewer
|
|
|
|
// has rendered.
|
|
|
|
await expect(pdfViewer).toBeAttached();
|
|
|
|
|
|
|
|
// Verify that the PDF viewer has rendered. This relies on how Chrome/Electron loads custom PDFs
|
|
|
|
// in an object.
|
|
|
|
// If this breaks due to an Electron upgrade,
|
|
|
|
// 1. manually verify that the PDF viewer has loaded and
|
|
|
|
// 2. replace this test with a screenshot comparison (https://playwright.dev/docs/test-snapshots)
|
|
|
|
await expect.poll(
|
|
|
|
() => pdfViewer.evaluate((handle) => {
|
|
|
|
const embed = (handle as HTMLObjectElement).contentDocument.querySelector('embed');
|
|
|
|
return !!embed;
|
|
|
|
}),
|
|
|
|
).toBe(true);
|
|
|
|
};
|
|
|
|
|
|
|
|
await expectToBeRendered();
|
|
|
|
|
|
|
|
// Should still render after switching editors
|
|
|
|
await mainScreen.noteEditor.toggleEditorsButton.click();
|
|
|
|
await mainScreen.noteEditor.richTextEditor.waitFor();
|
|
|
|
await mainScreen.noteEditor.toggleEditorsButton.click();
|
|
|
|
|
|
|
|
await expectToBeRendered();
|
|
|
|
});
|
|
|
|
|
|
|
|
test('preview pane should render video attachments', async ({ mainWindow, electronApp }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.createNewNote('Media attachments');
|
|
|
|
const editor = mainScreen.noteEditor;
|
|
|
|
|
|
|
|
await editor.focusCodeMirrorEditor();
|
|
|
|
await setFilePickerResponse(electronApp, [join(__dirname, 'resources', 'video.mp4')]);
|
|
|
|
await editor.attachFileButton.click();
|
|
|
|
|
2024-09-12 18:54:10 +02:00
|
|
|
const videoLocator = editor.getNoteViewerFrameLocator().locator('video');
|
2024-08-17 13:22:03 +02:00
|
|
|
const expectVideoToRender = async () => {
|
|
|
|
await expect(videoLocator).toBeSeekableMediaElement(6.9, 7);
|
|
|
|
};
|
|
|
|
|
|
|
|
await expectVideoToRender();
|
|
|
|
|
|
|
|
// Should be able to render again if the editor is closed and re-opened.
|
|
|
|
await mainScreen.noteEditor.toggleEditorsButton.click();
|
|
|
|
await mainScreen.noteEditor.richTextEditor.waitFor();
|
|
|
|
await mainScreen.noteEditor.toggleEditorsButton.click();
|
|
|
|
|
|
|
|
await expectVideoToRender();
|
|
|
|
});
|
|
|
|
|
2024-08-03 17:42:46 +02:00
|
|
|
test('arrow keys should navigate the toolbar', async ({ mainWindow }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.waitFor();
|
|
|
|
|
|
|
|
await mainScreen.createNewNote('Note 1');
|
|
|
|
await mainScreen.createNewNote('Note 2');
|
|
|
|
const noteEditor = mainScreen.noteEditor;
|
|
|
|
await noteEditor.focusCodeMirrorEditor();
|
|
|
|
|
|
|
|
// Escape, then Shift+Tab should focus the toolbar
|
|
|
|
await mainWindow.keyboard.press('Escape');
|
|
|
|
await mainWindow.keyboard.press('Shift+Tab');
|
|
|
|
|
|
|
|
// Should focus the first item by default, the "back" arrow (back to "Note 1")
|
|
|
|
const firstItemLocator = noteEditor.toolbarButtonLocator('Back');
|
|
|
|
await expect(firstItemLocator).toBeFocused();
|
|
|
|
|
|
|
|
// Left arrow should wrap to the end
|
|
|
|
await mainWindow.keyboard.press('ArrowLeft');
|
|
|
|
const lastItemLocator = noteEditor.toolbarButtonLocator('Toggle editors');
|
|
|
|
await expect(lastItemLocator).toBeFocused();
|
|
|
|
|
|
|
|
await mainWindow.keyboard.press('ArrowRight');
|
|
|
|
await expect(firstItemLocator).toBeFocused();
|
|
|
|
|
|
|
|
// ArrowRight should skip disabled items (Forward).
|
|
|
|
await mainWindow.keyboard.press('ArrowRight');
|
|
|
|
await expect(noteEditor.toolbarButtonLocator('Toggle external editing')).toBeFocused();
|
|
|
|
|
|
|
|
// Home/end should navigate to the first/last items
|
|
|
|
await mainWindow.keyboard.press('End');
|
|
|
|
await expect(lastItemLocator).toBeFocused();
|
|
|
|
|
|
|
|
await mainWindow.keyboard.press('Home');
|
|
|
|
await expect(firstItemLocator).toBeFocused();
|
|
|
|
});
|
2024-08-15 17:01:52 +02:00
|
|
|
|
|
|
|
test('should sync local search between the viewer and editor', async ({ mainWindow }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.waitFor();
|
|
|
|
const noteEditor = mainScreen.noteEditor;
|
|
|
|
|
|
|
|
await mainScreen.createNewNote('Note');
|
|
|
|
|
|
|
|
await noteEditor.focusCodeMirrorEditor();
|
|
|
|
|
|
|
|
await mainWindow.keyboard.type('# Testing');
|
|
|
|
await mainWindow.keyboard.press('Enter');
|
|
|
|
await mainWindow.keyboard.press('Enter');
|
|
|
|
await mainWindow.keyboard.type('This is a test of search. `Test inline code`');
|
|
|
|
|
2024-09-12 18:54:10 +02:00
|
|
|
const viewer = noteEditor.getNoteViewerFrameLocator();
|
2024-08-15 17:01:52 +02:00
|
|
|
await expect(viewer.locator('h1')).toHaveText('Testing');
|
|
|
|
|
|
|
|
const matches = viewer.locator('mark');
|
|
|
|
await expect(matches).toHaveCount(0);
|
|
|
|
|
|
|
|
await mainWindow.keyboard.press(process.platform === 'darwin' ? 'Meta+f' : 'Control+f');
|
|
|
|
await expect(noteEditor.editorSearchInput).toBeVisible();
|
|
|
|
|
|
|
|
await noteEditor.editorSearchInput.click();
|
|
|
|
await noteEditor.editorSearchInput.fill('test');
|
|
|
|
await mainWindow.keyboard.press('Enter');
|
|
|
|
|
|
|
|
// Should show at least one match in the viewer
|
|
|
|
await expect(matches).toHaveCount(3);
|
|
|
|
await expect(matches.first()).toBeAttached();
|
|
|
|
|
|
|
|
// Should show matches in code regions
|
|
|
|
await noteEditor.editorSearchInput.fill('inline code');
|
|
|
|
await mainWindow.keyboard.press('Enter');
|
|
|
|
await expect(matches).toHaveCount(1);
|
|
|
|
|
|
|
|
// Should continue searching after switching to view-only mode
|
|
|
|
await noteEditor.toggleEditorLayoutButton.click();
|
|
|
|
await noteEditor.toggleEditorLayoutButton.click();
|
|
|
|
await expect(noteEditor.codeMirrorEditor).not.toBeVisible();
|
|
|
|
await expect(noteEditor.editorSearchInput).not.toBeVisible();
|
|
|
|
await expect(noteEditor.viewerSearchInput).toBeVisible();
|
|
|
|
|
|
|
|
// Should stop searching after closing the search input
|
|
|
|
await noteEditor.viewerSearchInput.click();
|
|
|
|
await expect(matches).toHaveCount(1);
|
|
|
|
await mainWindow.keyboard.press('Escape');
|
|
|
|
await expect(noteEditor.viewerSearchInput).not.toBeVisible();
|
|
|
|
await expect(matches).toHaveCount(0);
|
|
|
|
|
|
|
|
// After showing the viewer again, search should still be hidden
|
|
|
|
await noteEditor.toggleEditorLayoutButton.click();
|
|
|
|
await expect(noteEditor.codeMirrorEditor).toBeVisible();
|
|
|
|
await expect(noteEditor.editorSearchInput).not.toBeVisible();
|
|
|
|
});
|
2024-09-12 18:54:10 +02:00
|
|
|
|
|
|
|
test('should move focus when the visible editor panes change', async ({ mainWindow, electronApp }) => {
|
|
|
|
const mainScreen = new MainScreen(mainWindow);
|
|
|
|
await mainScreen.waitFor();
|
|
|
|
const noteEditor = mainScreen.noteEditor;
|
|
|
|
|
|
|
|
await mainScreen.createNewNote('Note');
|
|
|
|
|
|
|
|
await noteEditor.focusCodeMirrorEditor();
|
|
|
|
await mainWindow.keyboard.type('test');
|
|
|
|
const focusInMarkdownEditor = noteEditor.codeMirrorEditor.locator(':focus');
|
|
|
|
await expect(focusInMarkdownEditor).toBeAttached();
|
|
|
|
|
|
|
|
const toggleEditorLayout = () => activateMainMenuItem(electronApp, 'Toggle editor layout');
|
|
|
|
|
|
|
|
// Editor only
|
|
|
|
await toggleEditorLayout();
|
|
|
|
await expect(noteEditor.noteViewerContainer).not.toBeVisible();
|
|
|
|
// Markdown editor should be focused
|
|
|
|
await expect(focusInMarkdownEditor).toBeAttached();
|
|
|
|
|
|
|
|
// Viewer only
|
|
|
|
await toggleEditorLayout();
|
|
|
|
await expect(noteEditor.codeMirrorEditor).not.toBeVisible();
|
|
|
|
// Viewer should be focused
|
|
|
|
await expect(noteEditor.noteViewerContainer).toBeFocused();
|
|
|
|
|
|
|
|
// Viewer and editor
|
|
|
|
await toggleEditorLayout();
|
|
|
|
await expect(noteEditor.noteViewerContainer).toBeAttached();
|
|
|
|
await expect(noteEditor.codeMirrorEditor).toBeVisible();
|
|
|
|
// Editor should be focused
|
|
|
|
await expect(focusInMarkdownEditor).toBeAttached();
|
|
|
|
});
|
2024-08-02 15:47:43 +02:00
|
|
|
});
|
|
|
|
|