oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1import AppKit
2import DocsKit
3import RastrilloApple
4import SwiftUI
5
6@MainActor private enum SmokeRun { static var started = false }
7
8@main struct DocumentWindowSmoke: App {
9 @State private var windows = DocsWindows()
10 var body: some Scene {
11 Window("Smoke Home", id: "home") { Launcher(windows: windows) }
12 WindowGroup("Smoke Document", id: "document", for: String.self) { _ in
13 DocsWorkspace(client: NativeHTTP(origin: URL(string: ProcessInfo.processInfo.environment["DOCS_SMOKE_ORIGIN"]!)!),
14 signOut: {}, documentID: "fixture", newDocument: ProcessInfo.processInfo.environment["DOCS_SMOKE_DRAFT"] == "1")
15 }.defaultSize(width: 1120, height: 820)
16 .restorationBehavior(.disabled)
17 .commands { DocsEditorCommands(); DocsWindowCommands(windows: windows) }
18 }
19}
20
21private struct Launcher: View {
22 let windows: DocsWindows
23 @Environment(\.openWindow) private var openWindow
24 var body: some View {
25 Text("Synthetic document window smoke test").frame(width: 400, height: 200)
26 .task {
27 guard !SmokeRun.started else { return }
28 SmokeRun.started = true
29 NSApp.setActivationPolicy(.regular)
30 openWindow(id: "document", value: "fixture")
31 if ProcessInfo.processInfo.environment["DOCS_SMOKE_DRAFT"] == "1" {
32 let start = ContinuousClock.now
33 var draftEditor: NSTextView?
34 var draftWindow: NSWindow?
35 for _ in 0..<20 {
36 if let window = NSApp.windows.first(where: { $0.title == docsMacString("Untitled document") }), let editor = window.contentView.flatMap(findEditor) {
37 draftWindow = window; draftEditor = editor; break
38 }
39 try? await Task.sleep(for: .milliseconds(50))
40 }
41 guard let window = draftWindow, let editor = draftEditor else { fatalError("Local editor waited for server creation") }
42 precondition(start.duration(to: .now) < .seconds(1))
43 NSApp.activate(ignoringOtherApps: true)
44 window.makeKeyAndOrderFront(nil); window.makeFirstResponder(editor)
45 editor.insertText("Instant draft", replacementRange: NSRange(location: 0, length: 0))
46 for _ in 0..<80 {
47 if window.title == "Window smoke fixture" { break }
48 try? await Task.sleep(for: .milliseconds(50))
49 }
50 precondition(window.title == "Window smoke fixture")
51 precondition(window.contentView.flatMap(findEditor) === editor, "Server attachment replaced the editor")
52 precondition(editor.string.contains("Instant draft"), "Creation lost locally typed text")
53 print("PASS: draft editor appears before delayed creation and keeps its text view and typing")
54 await exercise(window, editor: editor)
55 return
56 }
57 for _ in 0..<100 {
58 if let window = NSApp.windows.first(where: { $0.title == "Window smoke fixture" }),
59 let editor = window.contentView.flatMap(findEditor), !editor.string.isEmpty {
60 await exercise(window, editor: editor)
61 return
62 }
63 try? await Task.sleep(for: .milliseconds(100))
64 }
65 fatalError("Document editor did not load")
66 }
67 }
68 @MainActor private func findEditor(_ view: NSView) -> NSTextView? {
69 if let text = view as? NSTextView, text.accessibilityIdentifier() == "docs.editor" { return text }
70 return view.subviews.lazy.compactMap(findEditor).first
71 }
72 @MainActor private func menuItem(_ title: String, in menu: NSMenu) -> NSMenuItem? {
73 for item in menu.items {
74 if item.title == title { return item }
75 if let submenu = item.submenu, let match = menuItem(title, in: submenu) { return match }
76 }
77 return nil
78 }
79 @MainActor private func exercise(_ window: NSWindow, editor: NSTextView) async {
80 NSApp.activate(ignoringOtherApps: true)
81 window.makeKeyAndOrderFront(nil)
82 window.makeFirstResponder(editor)
83 editor.setSelectedRange(NSRange(location: 1, length: 2))
84 try? await Task.sleep(for: .milliseconds(500))
85 let original = editor.string
86 for step in 0..<8 {
87 try? await Task.sleep(for: .milliseconds(200))
88 guard let menu = NSApp.mainMenu, let initial = menuItem(docsMacString("Document inspector"), in: menu), let submenu = initial.menu else {
89 fatalError("Document inspector command unavailable")
90 }
91 submenu.delegate?.menuNeedsUpdate?(submenu)
92 submenu.update()
93 guard let item = menuItem(docsMacString("Document inspector"), in: menu), item.isEnabled else {
94 fatalError("Inspector command disabled in document window")
95 }
96 let beforeWidth = editor.enclosingScrollView!.frame.width
97 item.menu?.performActionForItem(at: item.menu!.index(of: item))
98 try? await Task.sleep(for: .milliseconds(200))
99 precondition(abs(editor.enclosingScrollView!.frame.width - beforeWidth) > 200, "Inspector did not change editor space")
100 window.setContentSize(NSSize(width: step.isMultiple(of: 2) ? 900 : 1200, height: 760))
101 try? await Task.sleep(for: .milliseconds(200))
102 window.contentView?.layoutSubtreeIfNeeded()
103 precondition(window.contentView.flatMap(findEditor) === editor, "Inspector recreated editor")
104 precondition(editor.string == original && editor.selectedRange() == NSRange(location: 1, length: 2), "Panel lost content or selection")
105 }
106 // Save while the title field is still active, without submitting Return.
107 if let menu = NSApp.mainMenu, let rename = menuItem("Rename document", in: menu) {
108 rename.menu?.delegate?.menuNeedsUpdate?(rename.menu!)
109 rename.menu?.update()
110 guard let currentRename = menuItem("Rename document", in: menu), currentRename.isEnabled else { fatalError("Rename disabled in writable document") }
111 currentRename.menu?.performActionForItem(at: currentRename.menu!.index(of: currentRename))
112 try? await Task.sleep(for: .milliseconds(200))
113 func findTitle(_ view: NSView) -> NSTextField? {
114 if let field = view as? NSTextField, field.isEditable { return field }
115 return view.subviews.lazy.compactMap(findTitle).first
116 }
117 guard let root = window.contentView?.superview, let field = findTitle(root) else { fatalError("Editable header title missing") }
118 field.selectText(nil)
119 guard let input = field.currentEditor() as? NSTextView else { fatalError("Title has no field editor") }
120 input.selectAll(nil); input.insertText("Renamed from header", replacementRange: input.selectedRange())
121 try? await Task.sleep(for: .milliseconds(200))
122 guard let save = menuItem(docsMacString("Save"), in: menu) else { fatalError("Save unavailable") }
123 save.menu?.performActionForItem(at: save.menu!.index(of: save))
124 try? await Task.sleep(for: .milliseconds(300))
125 precondition(window.title == "Renamed from header", "Save lost the active title input")
126 print("PASS: Save commits active header title without Return")
127 } else { fatalError("Rename command missing") }
128 window.makeFirstResponder(editor)
129 editor.setSelectedRange(NSRange(location: editor.string.utf16.count - 1, length: 0))
130 NSApp.activate(ignoringOtherApps: true)
131 window.makeKeyAndOrderFront(nil)
132 window.makeFirstResponder(editor)
133 editor.scrollRangeToVisible(editor.selectedRange())
134 try? await Task.sleep(for: .milliseconds(100))
135 func indicators(_ view: NSView) -> [NSView] {
136 (view is NSTextInsertionIndicator ? [view] : []) + view.subviews.flatMap(indicators)
137 }
138 func record(_ phase: String) {
139 let manager = editor.layoutManager!
140 manager.ensureLayout(for: editor.textContainer!)
141 let glyph = manager.glyphIndexForCharacter(at: min(editor.selectedRange().location, editor.string.utf16.count - 1))
142 let line = manager.lineFragmentRect(forGlyphAt: glyph, effectiveRange: nil)
143 let rect = editor.firstRect(forCharacterRange: editor.selectedRange(), actualRange: nil)
144 precondition(editor.textContainerOrigin == .zero, "Native caret origin drifted")
145 precondition(rect.height > 0, "Caret clipped outside editor bounds")
146 precondition(editor.bounds.maxY >= line.maxY, "Editor shrank below current line")
147 let visible = indicators(window.contentView!).filter { !$0.isHidden }
148 precondition(visible.count == 1, "Expected exactly one visible native insertion indicator; count=\(visible.count), phase=\(phase), editable=\(editor.isEditable), attached=\(editor.window === window), key=\(window.isKeyWindow), responder=\(String(describing: window.firstResponder))")
149 for indicator in visible {
150 let actual = indicator.convert(indicator.bounds, to: editor)
151 precondition(abs(actual.minY - line.minY) < 1, "Native insertion indicator disagrees with glyphs")
152 }
153
154 }
155 for character in "\nCaret stability after Enter.\nSecond line.\nThird line." {
156 window.makeKeyAndOrderFront(nil)
157 window.makeFirstResponder(editor)
158 let before = editor.selectedRange().location
159 let typed = character == "\n" ? "\r" : String(character)
160 let event = NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: [], timestamp: ProcessInfo.processInfo.systemUptime, windowNumber: window.windowNumber, context: nil, characters: typed, charactersIgnoringModifiers: typed, isARepeat: false, keyCode: character == "\n" ? 36 : 0)!
161 editor.keyDown(with: event)
162 record("immediate")
163 let immediate = editor.firstRect(forCharacterRange: editor.selectedRange(), actualRange: nil)
164 try? await Task.sleep(for: .milliseconds(100))
165 precondition(editor.selectedRange().location == before + String(character).utf16.count, "Typing moved selection unexpectedly")
166 record("settled")
167 let settled = editor.firstRect(forCharacterRange: editor.selectedRange(), actualRange: nil)
168 precondition(abs(immediate.minY - settled.minY) < 1, "Caret jumped after publication")
169 }
170 if let path = ProcessInfo.processInfo.environment["DOCS_SMOKE_SCREENSHOT"], let view = window.contentView,
171 let bitmap = view.bitmapImageRepForCachingDisplay(in: view.bounds) {
172 view.cacheDisplay(in: view.bounds, to: bitmap)
173 try? bitmap.representation(using: .png, properties: [:])?.write(to: URL(fileURLWithPath: path))
174 }
175 print("PASS: document loads, inspector toggles, window resizes, editor and selection persist, Enter and typing keep caret geometry stable")
176 NSApp.windows.first(where: { $0.title == "Smoke Home" })?.close()
177 window.makeKeyAndOrderFront(nil)
178 guard let menu = NSApp.mainMenu, let appMenu = menu.items.first?.submenu else { fatalError("App menu missing") }
179 appMenu.delegate?.menuNeedsUpdate?(appMenu)
180 appMenu.update()
181 guard let accounts = menuItem(docsMacString("Accounts"), in: appMenu), accounts.isEnabled,
182 let signOut = menuItem(docsMacString("Sign out"), in: appMenu), !signOut.isEnabled else {
183 fatalError("Account commands missing or unsafe in document window")
184 }
185 accounts.menu!.performActionForItem(at: accounts.menu!.index(of: accounts))
186 try? await Task.sleep(for: .milliseconds(300))
187 precondition(windows.accountsRequested, "Accounts action did not reach launcher")
188 precondition(NSApp.windows.contains { $0.title == "Smoke Home" && $0.isVisible }, "Accounts did not reopen Home")
189 print("PASS: account commands live in app menu and Accounts reopens closed Home")
190 NSApp.terminate(nil)
191 }
192}
193