import { test, expect } from './util/test'; import MainScreen from './models/MainScreen'; import { Second } from '@joplin/utils/time'; test.describe('sidebar', () => { test('should be able to create new folders', async ({ mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; for (let i = 0; i < 3; i++) { const title = `Test folder ${i}`; await sidebar.createNewFolder(title); await expect(sidebar.container.getByText(title)).toBeAttached(); } // The first folder should still be visible await expect(sidebar.container.getByText('Test folder 0')).toBeAttached(); }); test('should allow changing the focused folder with the arrow keys', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const folderAHeader = await sidebar.createNewFolder('Folder A'); await expect(folderAHeader).toBeVisible(); const folderBHeader = await sidebar.createNewFolder('Folder B'); await expect(folderBHeader).toBeVisible(); await folderBHeader.click(); await sidebar.forceUpdateSorting(electronApp); await folderBHeader.click(); await mainWindow.keyboard.press('ArrowUp'); await expect(mainWindow.locator(':focus')).toHaveText('Folder A'); await mainWindow.keyboard.press('ArrowDown'); await expect(mainWindow.locator(':focus')).toHaveText('Folder B'); await mainWindow.keyboard.press('ArrowUp'); await expect(mainWindow.locator(':focus')).toHaveText('Folder A'); await mainWindow.keyboard.press('ArrowUp'); await expect(mainWindow.locator(':focus')).toHaveText('All notes'); await mainWindow.keyboard.press('ArrowUp'); await expect(mainWindow.locator(':focus')).toHaveText(/NOTEBOOKS/i); await mainWindow.keyboard.press('ArrowDown'); await expect(mainWindow.locator(':focus')).toHaveText('All notes'); }); // Regression test for https://github.com/laurent22/joplin/issues/15029 test('should remain focused when navigating with the arrow keys', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; // Build the folder hierarchy: Navigating upwards through the list // should transition from a notebook with more notes to a notebook with // fewer notes. const folderAHeader = await sidebar.createNewFolder('Folder A'); await mainScreen.createNewNote('Test'); await expect(folderAHeader).toBeVisible(); const folderBHeader = await sidebar.createNewFolder('Folder B'); await mainScreen.createNewNote('Test 2'); await mainScreen.createNewNote('Test 3'); const folderCHeader = await sidebar.createNewFolder('Folder C'); const folderDHeader = await sidebar.createNewFolder('Folder D'); await folderBHeader.dragTo(folderAHeader); await folderCHeader.dragTo(folderAHeader); // Should have the correct initial state await sidebar.forceUpdateSorting(electronApp); await sidebar.expectToHaveDepths([ [folderAHeader, 2], [folderBHeader, 3], [folderCHeader, 3], [folderDHeader, 2], ]); const assertFocused = async (title: RegExp) => { await expect(mainWindow.locator(':focus')).toHaveText(title); // Pause to help check that focus is stable. This is present to help this test more reliably detect // timing-related issues. await mainWindow.waitForTimeout(Second); await expect(mainWindow.locator(':focus')).toHaveText(title); }; await folderDHeader.click(); // Focus should remain on the correct folder header while navigating await mainWindow.keyboard.press('ArrowUp'); await assertFocused(/^Folder C/); await mainWindow.keyboard.press('ArrowUp'); await assertFocused(/^Folder B/); await mainWindow.keyboard.press('ArrowUp'); await assertFocused(/^Folder A/); }); test('should allow changing the focused folder by pressing the first character of the title', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const folderAHeader = await sidebar.createNewFolder('1-Test A'); await expect(folderAHeader).toBeVisible(); const folderBHeader = await sidebar.createNewFolder('Folder b'); await expect(folderBHeader).toBeVisible(); await folderBHeader.click(); await sidebar.forceUpdateSorting(electronApp); await folderBHeader.click(); await mainWindow.keyboard.type('1'); await expect(mainWindow.locator(':focus')).toHaveText('1-Test A'); await mainWindow.keyboard.type('F'); await expect(mainWindow.locator(':focus')).toHaveText('Folder b'); await mainWindow.keyboard.type('A'); await expect(mainWindow.locator(':focus')).toHaveText('All notes'); // Should be case-insensitive await mainWindow.keyboard.type('f'); await expect(mainWindow.locator(':focus')).toHaveText('Folder b'); }); test('left/right arrow keys should expand/collapse notebooks', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; // Build the folder hierarchy const folderAHeader = await sidebar.createNewFolder('Folder A'); await expect(folderAHeader).toBeVisible(); const folderBHeader = await sidebar.createNewFolder('Folder B'); const folderCHeader = await sidebar.createNewFolder('Folder C'); const folderDHeader = await sidebar.createNewFolder('Folder D'); await folderBHeader.dragTo(folderAHeader); await folderCHeader.dragTo(folderAHeader); await folderDHeader.dragTo(folderCHeader); // Folders should have correct initial levels await sidebar.expectToHaveDepths([ [folderAHeader, 2], [folderBHeader, 3], [folderCHeader, 3], [folderDHeader, 4], ]); await sidebar.forceUpdateSorting(electronApp); await folderBHeader.click(); // Pressing [left] on a folder with no children should jump to its parent await mainWindow.keyboard.press('ArrowLeft'); await expect(mainWindow.locator(':focus')).toHaveText('Folder A'); // Pressing [left] again should collapse the folder await expect(folderAHeader).toHaveJSProperty('ariaExpanded', 'true'); await mainWindow.keyboard.press('ArrowLeft'); await expect(folderAHeader).toHaveJSProperty('ariaExpanded', 'false'); // Should still be focused await expect(mainWindow.locator(':focus')).toHaveText('Folder A'); // Pressing [right] on a collapsed folder should expand it await mainWindow.keyboard.press('ArrowRight'); await expect(folderAHeader).toHaveJSProperty('ariaExpanded', 'true'); // Pressing [right] again should move to the next item await mainWindow.keyboard.press('ArrowRight'); await expect(mainWindow.locator(':focus')).toHaveText('Folder B'); }); test('should allow changing the parent of a folder by drag-and-drop', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const parentFolderHeader = await sidebar.createNewFolder('Parent folder'); await expect(parentFolderHeader).toBeVisible(); const childFolderHeader = await sidebar.createNewFolder('Child folder'); await expect(childFolderHeader).toBeVisible(); await sidebar.forceUpdateSorting(electronApp); await expect(childFolderHeader).toBeVisible(); await childFolderHeader.dragTo(parentFolderHeader); // Verify that it's now a child folder -- expand and collapse the parent await expect(parentFolderHeader).toHaveJSProperty('ariaExpanded', 'true'); const toggleButton = parentFolderHeader.getByRole('button', { name: /^(Expand|Collapse)/ }); await toggleButton.click(); // Should be collapsed await expect(childFolderHeader).not.toBeAttached(); await expect(parentFolderHeader).toHaveJSProperty('ariaExpanded', 'false'); await toggleButton.click(); // Should be possible to move back to the root const rootFolderHeader = sidebar.container.getByText('Notebooks'); await childFolderHeader.dragTo(rootFolderHeader); await expect(toggleButton).not.toBeVisible(); }); test('all notes section should list all notes', async ({ electronApp, mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const testFolderA = await sidebar.createNewFolder('Folder A'); await expect(testFolderA).toBeAttached(); await sidebar.forceUpdateSorting(electronApp); await mainScreen.createNewNote('A note in Folder A'); await expect(mainWindow.getByText('A note in Folder A')).toBeAttached(); await mainScreen.createNewNote('Another note in Folder A'); const testFolderB = await sidebar.createNewFolder('Folder B'); await expect(testFolderB).toBeAttached(); await mainScreen.createNewNote('A note in Folder B'); const allNotesButton = sidebar.container.getByText('All notes'); await allNotesButton.click(); await expect(mainWindow.getByText('A note in Folder A')).toBeAttached(); await expect(mainWindow.getByText('Another note in Folder A')).toBeAttached(); await expect(mainWindow.getByText('A note in Folder B')).toBeAttached(); }); test('double-clicking should collapse/expand folders in the sidebar', async ({ mainWindow }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const testFolderA = await sidebar.createNewFolder('Folder A'); const testFolderB = await sidebar.createNewFolder('Folder B'); // Convert folder B to a subfolder await testFolderB.dragTo(testFolderA); await expect(testFolderB).toBeVisible(); // Collapse await testFolderA.dblclick(); await expect(testFolderB).not.toBeVisible(); // Expand await testFolderA.dblclick(); await expect(testFolderB).toBeVisible(); }); test('should be possible to select, then deselect, multiple folders with cmd-click', async ({ mainWindow, electronApp }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const folderA = await sidebar.createNewFolder('Folder A'); const folderB = await sidebar.createNewFolder('Folder B'); const folderC = await sidebar.createNewFolder('Folder C'); const folderD = await sidebar.createNewFolder('Folder D'); await sidebar.forceUpdateSorting(electronApp); await folderA.click(); await folderB.click({ modifiers: ['ControlOrMeta'] }); await folderC.click({ modifiers: ['ControlOrMeta'] }); await expect(folderA).toBeSelected(); await expect(folderB).toBeSelected(); await expect(folderC).toBeSelected(); await expect(folderD).toHaveJSProperty('ariaSelected', 'false'); // Should be able to deselect up to two folders await folderA.click({ modifiers: ['ControlOrMeta'] }); await expect(folderA).toHaveJSProperty('ariaSelected', 'false'); await folderB.click({ modifiers: ['ControlOrMeta'] }); await expect(folderB).toHaveJSProperty('ariaSelected', 'false'); // Should not be possible to deselect the last folder await folderC.click({ modifiers: ['ControlOrMeta'] }); await expect(folderC).toBeSelected(); }); test('should be possible to move multiple folders at once with drag and drop', async ({ mainWindow, electronApp }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const folderA = await sidebar.createNewFolder('Folder A'); const folderB = await sidebar.createNewFolder('Folder B'); const folderC = await sidebar.createNewFolder('Folder C'); const folderD = await sidebar.createNewFolder('Folder D'); await sidebar.forceUpdateSorting(electronApp); await folderB.click(); await folderC.click({ modifiers: ['ControlOrMeta'] }); await expect(folderB).toBeSelected(); await expect(folderC).toBeSelected(); await folderB.dragTo(folderA); // Should have made folder B **and folder C** subfolders of testFolderA await sidebar.expectToHaveDepths([ [folderA, 2], [folderB, 3], [folderC, 3], [folderD, 2], ]); }); test('should not move selected folders when dragging an unselected folder', async ({ mainWindow, electronApp }) => { const mainScreen = await new MainScreen(mainWindow).setup(); const sidebar = mainScreen.sidebar; const testFolderA = await sidebar.createNewFolder('Folder A'); const testFolderB = await sidebar.createNewFolder('Folder B'); const testFolderC = await sidebar.createNewFolder('Folder C'); await sidebar.forceUpdateSorting(electronApp); await testFolderB.click(); await testFolderC.click({ modifiers: ['ControlOrMeta'] }); await expect(testFolderB).toBeSelected(); await expect(testFolderC).toBeSelected(); await testFolderA.dragTo(testFolderB); await sidebar.expectToHaveDepths([ [testFolderB, 2], [testFolderA, 3], [testFolderC, 2], ]); }); });