refactor: split sidebar filetree command runtime

This commit is contained in:
lix-2026
2026-05-26 02:20:46 +08:00
parent 7816ab7ae2
commit 7909b3f9be
6 changed files with 1780 additions and 1553 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5
View File
@@ -138,6 +138,10 @@ pub fn build_router(state: AppState) -> Router {
"/api/mnote-browser-runtime/sidebar-filetree-open-runtime.js",
get(web_shell::sidebar_filetree_open_runtime_asset),
)
.route(
"/api/mnote-browser-runtime/sidebar-filetree-command-runtime.js",
get(web_shell::sidebar_filetree_command_runtime_asset),
)
.route(
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
get(web_shell::sidebar_attachment_open_runtime_asset),
@@ -598,6 +602,7 @@ mod tests {
"/api/mnote-browser-runtime/sidebar-workspace-runtime.js",
"/api/mnote-browser-runtime/sidebar-tree-live-apply-runtime.js",
"/api/mnote-browser-runtime/sidebar-filetree-open-runtime.js",
"/api/mnote-browser-runtime/sidebar-filetree-command-runtime.js",
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
"/api/mnote-browser-runtime/sidebar-tree-runtime.js",
"/api/mnote-browser-runtime/tree-live-controller.js",
@@ -784,6 +784,20 @@ pub async fn sidebar_filetree_open_runtime_asset() -> Response {
.unwrap_or_else(|_| Response::new(Body::empty()))
}
pub async fn sidebar_filetree_command_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-filetree-command-runtime.js");
Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
"application/javascript; charset=utf-8",
)
.header(header::CACHE_CONTROL, "no-store")
.header(HEADER_MNOTE_WEB_OWNER, "mnote-web")
.body(Body::from(JS))
.unwrap_or_else(|_| Response::new(Body::empty()))
}
pub async fn sidebar_attachment_open_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-attachment-open-runtime.js");
Response::builder()
+99 -72
View File
@@ -203,6 +203,8 @@ mod tests {
include_str!("../../../browser/sidebar-tree-live-apply-runtime.js");
const SIDEBAR_FILETREE_OPEN_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-filetree-open-runtime.js");
const SIDEBAR_FILETREE_COMMAND_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-filetree-command-runtime.js");
const SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS: &str =
include_str!("../../../browser/sidebar-attachment-open-runtime.js");
const SIDEBAR_SHELL_RUNTIME_JS: &str =
@@ -313,7 +315,7 @@ mod tests {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.internal-drop"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.external-drop"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("beginFileTreeInlineRename"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree-rename-input"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("tree-rename-input"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebarFileTreeClipboard"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pasteSidebarFileTreeClipboard"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX"));
@@ -578,16 +580,18 @@ mod tests {
fn sidebar_filetree_runtime_helpers_are_externalized_with_inline_fallback() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function fileTreeRuntimeFunction"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.__mnoteFileTreeRuntime"));
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('fileTreeRowDocumentId')")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('fileTreeRowAssetId')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeRowDocumentId')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeRowAssetId')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeRowLocalRelativePath')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeAssetDownloadDetail')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('fileTreeRowsByRowIds')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('fileTreeChildCount')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeRowsByRowIds')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeRuntimeFunction('fileTreeChildCount')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('revealFileTreeRow')"));
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('revealFileTreeAssetRow')")
@@ -631,7 +635,8 @@ mod tests {
.contains("fileTreeRuntimeFunction('applyLocalFolderSidebarSnapshot')"));
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
.contains("fileTreeRuntimeFunction('markLocalFolderWatchApplied')"));
let document_id_body = js_function_body(SIDEBAR_TREE_RUNTIME_JS, "fileTreeRowDocumentId");
let document_id_body =
js_function_body(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS, "fileTreeRowDocumentId");
assert!(
document_id_body.contains("data-document-id")
&& document_id_body.contains("data-doc-id"),
@@ -759,24 +764,29 @@ mod tests {
fn sidebar_filetree_selection_runtime_helpers_are_externalized_with_inline_fallback() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function fileTreeSelectionRuntimeFunction"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.__mnoteFileTreeSelectionRuntime"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeSelectionRuntimeFunction('visibleFileTreeRows')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeSelectionRuntimeFunction('syncSidebarFileTreeSelection')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeSelectionRuntimeFunction('selectSidebarFileTreeRow')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeSelectionRuntimeFunction('selectedSidebarFileTreeRowIdsForDrag')"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("fileTreeSelectionRuntimeFunction('selectedSidebarFileTreeRows')"));
let select_body = js_function_body(SIDEBAR_TREE_RUNTIME_JS, "selectSidebarFileTreeRow");
let select_body = js_function_body(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS,
"selectSidebarFileTreeRow",
);
assert!(
select_body.contains("shiftKey && sidebarFileTreeSelection.anchorRowId")
&& select_body.contains("ctrlKey"),
"inline fallback 必须保留 shift range 与 ctrl/meta toggle"
);
let select_document_body =
js_function_body(SIDEBAR_TREE_RUNTIME_JS, "selectSidebarFileTreeDocument");
let select_document_body = js_function_body(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS,
"selectSidebarFileTreeDocument",
);
assert!(
select_document_body
.contains("selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false })"),
@@ -800,8 +810,10 @@ mod tests {
#[test]
fn sidebar_tree_runtime_focuses_restored_local_folder_row() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("applyPendingLocalFolderRestoreFocusOnce"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote.pendingLocalFolderRestoreFiletreeRowId"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-local-folder-restore-focused-row-id"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("mnote.pendingLocalFolderRestoreFiletreeRowId"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("data-mnote-local-folder-restore-focused-row-id"));
}
#[test]
@@ -885,29 +897,29 @@ mod tests {
#[test]
fn sidebar_tree_runtime_renders_context_menu_and_scoped_title_updates() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openTreeContextMenu"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote-tree-context-menu"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("复制访问链接"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("删除到垃圾桶"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("mnote-tree-context-menu"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("复制访问链接"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("删除到垃圾桶"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function createFileTreeFolder"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("action: 'create_folder'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function fileTreeMenuTargetParentId"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("if (action === 'new-folder')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeCopyPath(detail, trigger)"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function fileTreeMenuTargetParentId"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("if (action === 'new-folder')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("fileTreeCopyPath(detail, trigger)"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("if (detail.contextKind === 'filetree' && action === 'download')"));
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("downloadSelectedFileTreeAssetRows(detail, trigger)")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function selectedSidebarFileTreeRowsForDownload"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("downloadSelectedFileTreeAssetRows(detail, trigger)"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("function selectedSidebarFileTreeRowsForDownload"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("function selectedSidebarFileTreeAssetRowsForDownload"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-local-relative-path"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-local-relative-path"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("targetRelativePath"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains(
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
"recordFileTreeAction(downloads.length > 1 ? 'bulk-download' : 'download', primary)"
));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-filetree-download-count"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function triggerBrowserDownload"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-filetree-download-count"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function triggerBrowserDownload"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openEditorAttachmentMenu(editorAttachmentLink, editorAttachmentLink, { x: event.clientX, y: event.clientY });"));
assert!(SIDEBAR_TREE_RUNTIME_JS
.contains("var localFilePath = localFilePathFromAssetId(assetId);"));
@@ -974,36 +986,39 @@ mod tests {
#[test]
fn sidebar_tree_delete_to_trash_dispatches_archive_not_purge() {
let delete_trash_start = SIDEBAR_TREE_RUNTIME_JS
let delete_trash_start = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.find("if (action === 'delete-trash'")
.expect("delete-trash branch");
let delete_trash_end = SIDEBAR_TREE_RUNTIME_JS[delete_trash_start..]
let delete_trash_end = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[delete_trash_start..]
.find("function appendTreeContextMenuButton")
.map(|offset| delete_trash_start + offset)
.expect("delete-trash branch end");
let delete_trash_branch = &SIDEBAR_TREE_RUNTIME_JS[delete_trash_start..delete_trash_end];
let delete_trash_branch =
&SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[delete_trash_start..delete_trash_end];
assert!(delete_trash_branch.contains("action: 'archive'"));
assert!(!delete_trash_branch.contains("action: 'purge'"));
}
#[test]
fn sidebar_filetree_delete_keys_support_mixed_doc_and_asset_selection() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function deleteSelectedSidebarFileTreeRows"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("function deleteSelectedSidebarFileTreeRows"));
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("event.key === 'Delete' || event.key === 'Backspace'")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("selectedFileTreeRows.length > 1"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("selectedFileTreeRows.length > 1"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("deleteSelectedSidebarFileTreeRows(trigger || document.body)"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains(
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
"currentSourceKind() === 'local_folder' && fileTreeRowKind(row) === 'asset'"
));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("if (currentSourceKind() === 'local_folder')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("documentId: fileAssetIds[lf]"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/media/batch"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/mindmap/"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/tables/"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("确认删除选中的 "));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("if (currentSourceKind() === 'local_folder')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("documentId: fileAssetIds[lf]"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/media/batch"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/mindmap/"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/tables/"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("确认删除选中的 "));
}
#[test]
@@ -1011,23 +1026,31 @@ mod tests {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function blockReadonlyFileTreeAction"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-filetree-readonly-blocked"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-filetree-readonly-message"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("recordFileTreeActionStatus('blocked'"));
assert!(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("recordFileTreeActionStatus('blocked'")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/tree/filetree/drop-preflight"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('paste'"));
assert!(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("ensureFileTreeWritableTarget('paste'")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'"));
}
#[test]
fn sidebar_filetree_command_context_helper_functions_exist() {
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function buildSidebarFileTreeContext"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function evaluateSidebarFileTreeWhen"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("__mnoteFileTreeContextMenuRuntime"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'workspace.sourceKind'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'workspace.readonly'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'tree.focusKind'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'tree.selectionCount'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'tree.selectionResourceKind'"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-workspace-readonly"));
assert!(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function buildSidebarFileTreeContext")
);
assert!(
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function evaluateSidebarFileTreeWhen")
);
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("__mnoteFileTreeContextMenuRuntime"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'workspace.sourceKind'"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'workspace.readonly'"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.focusKind'"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.selectionCount'"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.selectionResourceKind'"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-workspace-readonly"));
}
#[test]
@@ -1045,11 +1068,11 @@ mod tests {
fn sidebar_filetree_context_menu_items_have_when_for_readonly() {
// FileTree 分支的写操作必须带 when: '!workspace.readonly'。
assert!(
SIDEBAR_TREE_RUNTIME_JS.contains("when: '!workspace.readonly'"),
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("when: '!workspace.readonly'"),
"上下文菜单写操作应携带 when: '!workspace.readonly'"
);
// Verify at least one delete-trash item has the when expression
let delete_trash_items = SIDEBAR_TREE_RUNTIME_JS
let delete_trash_items = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.match_indices("'delete-trash'")
.count();
assert!(delete_trash_items > 0, "应至少有一个 delete-trash 菜单项");
@@ -1066,14 +1089,14 @@ mod tests {
#[test]
fn sidebar_filetree_local_table_bulk_delete_uses_tree_command() {
let table_loop_start = SIDEBAR_TREE_RUNTIME_JS
let table_loop_start = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.find("for (var t = 0; t < plan.tableRows.length; t += 1)")
.expect("table bulk delete loop");
let table_loop_end = SIDEBAR_TREE_RUNTIME_JS[table_loop_start..]
let table_loop_end = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[table_loop_start..]
.find("sidebarFileTreeSelection.selectedRowIds")
.map(|offset| table_loop_start + offset)
.expect("table bulk delete loop end");
let table_loop = &SIDEBAR_TREE_RUNTIME_JS[table_loop_start..table_loop_end];
let table_loop = &SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[table_loop_start..table_loop_end];
assert!(table_loop.contains("currentSourceKind() === 'local_folder'"));
assert!(table_loop.contains("dispatchTreeCommand(trigger || tableRow"));
assert!(table_loop.contains("action: 'archive'"));
@@ -1138,21 +1161,25 @@ mod tests {
assert!(
SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("params.set('mode', input.mode || 'view');")
);
assert!(SIDEBAR_TREE_RUNTIME_JS.contains(
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
"{ action: 'open-edit-mode', icon: 'edit_note', label: '使用编辑模式打开' }"
));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("{ action: 'open-edit-mode', icon: 'edit_note', label: '弹窗编辑' }"));
assert!(SIDEBAR_TREE_RUNTIME_JS
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("{ action: 'new-window-edit', icon: 'open_in_new', label: '新窗口编辑' }"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function withOfficeEditModeGuard(callback)"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-last-office-edit-mode-requested"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("function withOfficeEditModeGuard(callback)"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("data-mnote-last-office-edit-mode-requested"));
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
.contains("function openEditorAttachmentEditTab(detail)"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("void openEditorAttachmentEditTab(detail);"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("void openEditorAttachmentEditTab(detail);"));
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
.contains("openLocalOfficeFileInActiveTab(detail, 'edit')"));
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openEditorAttachmentNewWindow(detail, 'edit')"));
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
.contains("openEditorAttachmentNewWindow(detail, 'edit')"));
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("forceEditMode ? 'edit' : 'view'"));
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
.contains("url.searchParams.set('mode', requestedMode);"));