feat: restore global search in app header
This commit is contained in:
@@ -114,6 +114,20 @@ async function runReadyScenario(cdp, url) {
|
||||
await assertText(cdp, 'Верстак', 'ready: brand visible')
|
||||
await assertEval(cdp, `document.querySelectorAll('.nav-item').length >= 3`, 'ready: system navigation rendered')
|
||||
await assertEval(cdp, `document.querySelectorAll('.tree-item').length >= 2`, 'ready: workspace tree rendered')
|
||||
await waitForSelector(cdp, '.global-search-input')
|
||||
await assertEval(cdp, `document.querySelector('.global-search-input')?.placeholder === 'Поиск по Верстаку...'`, 'header: global search is visible')
|
||||
await dispatchShortcut(cdp, 'k', true)
|
||||
await assertEval(cdp, `document.activeElement === document.querySelector('.global-search-input')`, 'header: Ctrl+K focuses global search')
|
||||
await setInputValue(cdp, '.global-search-input', 'Smoke')
|
||||
await waitForSelector(cdp, '.global-search-results')
|
||||
await assertText(cdp, 'Проект · Верстак / Smoke Project', 'search: node result shows type and breadcrumbs')
|
||||
await assertText(cdp, 'Заметка · Верстак / Smoke Project', 'search: note result shows type and breadcrumbs')
|
||||
await assertText(cdp, 'Файл · Верстак / Smoke Project / Assets', 'search: file result shows type and breadcrumbs')
|
||||
await assertText(cdp, 'Ссылка · Верстак / Smoke Project', 'search: link result shows type and breadcrumbs')
|
||||
await clickText(cdp, '.global-search-result', 'Smoke note')
|
||||
await assertText(cdp, 'Rendered by GUI smoke.', 'search: note result opens note editor')
|
||||
await dispatchBodyKeydown(cdp, 'Backspace')
|
||||
await waitForGone(cdp, '.note-editor')
|
||||
await screenshot(cdp, 'ready-main.png')
|
||||
|
||||
await click(cdp, '.sidebar-settings-btn')
|
||||
@@ -136,12 +150,13 @@ async function runReadyScenario(cdp, url) {
|
||||
|
||||
await clickText(cdp, '.nav-item', 'Неразобранное')
|
||||
await assertText(cdp, 'Неразобранное', 'inbox: system view opens')
|
||||
await assertEval(cdp, `!document.querySelector('.inbox-header h2')`, 'inbox: screen title is not duplicated inside body')
|
||||
await assertText(cdp, 'Inbox Smoke Item', 'inbox: captured item visible')
|
||||
await assertText(cdp, 'Текст', 'inbox: capture kind visible')
|
||||
await assertText(cdp, 'Буфер обмена', 'inbox: capture source visible')
|
||||
await assertEval(cdp, `!document.querySelector('.inbox-screen')?.innerText.includes('Manual Root Item')`, 'inbox: manual root is hidden')
|
||||
await setClipboardText(cdp, 'https://example.test/from-clipboard')
|
||||
await clickText(cdp, '.inbox-header .btn', 'Вставить из буфера')
|
||||
await clickText(cdp, '.header-actions .btn', 'Вставить из буфера')
|
||||
await assertText(cdp, 'example.test', 'inbox: clipboard URL captured')
|
||||
await assertEval(cdp, `
|
||||
(() => {
|
||||
@@ -171,7 +186,7 @@ async function runReadyScenario(cdp, url) {
|
||||
await waitForGone(cdp, '.modal-overlay')
|
||||
await assertEval(cdp, `!document.querySelector('.inbox-screen')?.innerText.includes('example.test')`, 'inbox: assigned link leaves inbox')
|
||||
await setClipboardText(cdp, 'mirv.top')
|
||||
await clickText(cdp, '.inbox-header .btn', 'Вставить из буфера')
|
||||
await clickText(cdp, '.header-actions .btn', 'Вставить из буфера')
|
||||
await assertText(cdp, 'mirv.top', 'inbox: bare domain captured as URL')
|
||||
await clickInboxItemButton(cdp, 'mirv.top', 'Открыть')
|
||||
await assertEval(cdp, `window.__VERSTAK_GUI_SMOKE__.state.openedUrls.includes('https://mirv.top')`, 'inbox: bare domain opens normalized URL')
|
||||
@@ -219,6 +234,7 @@ async function runReadyScenario(cdp, url) {
|
||||
await clickText(cdp, '.note-editor-actions .btn', 'Закрыть')
|
||||
|
||||
await clickText(cdp, '.nav-item', 'Корзина')
|
||||
await assertEval(cdp, `!document.querySelector('.trash-header h2')`, 'trash: screen title is not duplicated inside body')
|
||||
await assertText(cdp, 'Trash Smoke Folder', 'trash: deleted node visible')
|
||||
await assertEval(cdp, `!document.body.innerText.includes('.verstak/trash') && !document.body.innerText.includes('node-trash_Trash-Smoke-Folder')`, 'trash: physical implementation entries are hidden')
|
||||
await click(cdp, '.trash-row.folder .trash-row-icon')
|
||||
@@ -324,10 +340,12 @@ async function runReadyScenario(cdp, url) {
|
||||
|
||||
await clickText(cdp, '.nav-item', 'Сегодня')
|
||||
await assertText(cdp, 'Сегодня', 'today: system view opens')
|
||||
await assertEval(cdp, `!document.querySelector('.today-header h2')`, 'today: screen title is not duplicated inside body')
|
||||
await assertText(cdp, 'Smoke Project', 'today: dashboard data visible')
|
||||
|
||||
await clickText(cdp, '.nav-item', 'Журнал')
|
||||
await waitForSelector(cdp, '.journal-screen')
|
||||
await assertEval(cdp, `!document.querySelector('.journal-screen h2')`, 'journal: screen title is not duplicated inside body')
|
||||
await clickText(cdp, '.journal-tab', 'Журнал работы')
|
||||
await screenshot(cdp, 'journal.png')
|
||||
await assertEval(cdp, `document.body.innerText.toLowerCase().includes('фильтры')`, 'journal: filter section visible')
|
||||
@@ -335,6 +353,7 @@ async function runReadyScenario(cdp, url) {
|
||||
|
||||
await clickText(cdp, '.nav-item', 'Активность')
|
||||
await assertText(cdp, 'Активность', 'activity feed: system view opens')
|
||||
await assertEval(cdp, `!document.querySelector('.activity-feed-header h2')`, 'activity: screen title is not duplicated inside body')
|
||||
await assertText(cdp, 'Smoke activity', 'activity feed: event visible')
|
||||
|
||||
await click(cdp, '.nav-add-btn')
|
||||
@@ -478,6 +497,24 @@ async function dispatchBodyKeydown(cdp, key) {
|
||||
await sleep(150)
|
||||
}
|
||||
|
||||
async function dispatchShortcut(cdp, key, ctrlKey = false) {
|
||||
await cdp.send('Runtime.evaluate', {
|
||||
expression: `
|
||||
(() => {
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: ${JSON.stringify(key)},
|
||||
ctrlKey: ${ctrlKey ? 'true' : 'false'},
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
})()
|
||||
`,
|
||||
returnByValue: true,
|
||||
})
|
||||
await sleep(150)
|
||||
}
|
||||
|
||||
async function dispatchMouseBack(cdp) {
|
||||
await cdp.send('Runtime.evaluate', {
|
||||
expression: `
|
||||
@@ -891,6 +928,7 @@ function wailsMockSource() {
|
||||
};
|
||||
|
||||
const fileNodeDetails = {
|
||||
'note-1': { id: 'note-1', title: 'Smoke note', type: 'note', parent_id: 'node-project', createdAt: now, has_children: false },
|
||||
'file-folder': { id: 'file-folder', title: 'Assets', type: 'folder', parent_id: 'node-project', createdAt: now, has_children: false, children: [] },
|
||||
'file-brief': { id: 'file-brief', title: 'brief.md', type: 'file', parent_id: 'node-project', createdAt: now, has_children: false },
|
||||
'file-nested': { id: 'file-nested', title: 'nested.txt', type: 'file', parent_id: 'file-folder', createdAt: now, has_children: false },
|
||||
@@ -1164,6 +1202,15 @@ function wailsMockSource() {
|
||||
SetTemplateEnabled: async () => true,
|
||||
GetNodeDetail: async (id) => clone(findNode(id) || fileNodeDetails[id] || null),
|
||||
GetNodeTitle: async (id) => findNode(id)?.title || '',
|
||||
Search: async (query) => {
|
||||
const q = String(query || '').toLowerCase();
|
||||
return [
|
||||
{ nodeId: 'node-project', title: 'Smoke Project', type: 'project', path: 'Верстак / Smoke Project', snippet: '' },
|
||||
{ nodeId: 'note-1', title: 'Smoke note', type: 'note', path: 'Верстак / Smoke Project', snippet: 'Rendered by GUI smoke.' },
|
||||
{ nodeId: 'file-brief', title: 'brief.md', type: 'file', path: 'Верстак / Smoke Project / Assets', snippet: '' },
|
||||
{ nodeId: 'link-smoke', title: 'example.test', type: 'link', path: 'Верстак / Smoke Project', snippet: 'https://example.test' },
|
||||
].filter((item) => (item.title + ' ' + item.snippet + ' ' + item.path).toLowerCase().includes(q));
|
||||
},
|
||||
SearchNodes: async (query) => allNodes().filter((node) => node.title.toLowerCase().includes(String(query || '').toLowerCase())).map((node) => ({ id: node.id, title: node.title, path: '/Smoke/' + node.title, type: node.type })),
|
||||
CreateNodeFromTemplate: async (parentId, title, templateId) => {
|
||||
const id = 'node-created-' + Date.now();
|
||||
|
||||
Reference in New Issue
Block a user