oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2import RastrilloApple
3import XCTest
4@testable import DocsKit
5
6final class DocumentDraftTests: XCTestCase {
7 @MainActor func testImmediateDraftLostAcknowledgementRenameAndDiscard() async throws {
8 guard let origin = ProcessInfo.processInfo.environment["DOCS_DRAFT_TEST_ORIGIN"] else { throw XCTSkip("Run test-document-drafts.py") }
9 let client = NativeHTTP(origin: URL(string: origin)!)
10 let model = DocsModel(client: client, draft: true)
11 XCTAssertTrue(model.hasUnsavedChanges, "Protect the window before the first suspension")
12 let start = ContinuousClock.now
13 await model.newDraft()
14 XCTAssertLessThan(start.duration(to: .now), .milliseconds(500), "Opening cannot wait for the server")
15 XCTAssertNotNil(model.projection)
16 XCTAssertTrue(model.isDraft)
17 let identity = model.editorIdentity
18 for command in [
19 ["op": JSONValue.string("display_edit"), "at": .number(0), "length": .number(0), "text": .string("Draft text!")],
20 ["op": .string("display_edit"), "at": .number(10), "length": .number(1), "text": .string("")]
21 ] {
22 model.didRender(model.projectionGeneration)
23 XCTAssertTrue(model.enqueue(command))
24 while model.hasPendingInput { try await Task.sleep(for: .milliseconds(5)) }
25 }
26 model.rename("My document")
27 let deadline = ContinuousClock.now.advanced(by: .seconds(5))
28 while model.creatingDocument && .now < deadline { try await Task.sleep(for: .milliseconds(10)) }
29 XCTAssertTrue(model.isDraft, "The fixture drops the first creation acknowledgement")
30 XCTAssertEqual(model.projection?.children.map(\.plainText), ["Draft text"])
31 await model.retry()
32 let saved = await model.saveAll()
33 XCTAssertTrue(saved)
34 try await Task.sleep(for: .milliseconds(1000)) // Let the stale metadata reply finish.
35 XCTAssertEqual(model.editorIdentity, identity)
36 XCTAssertEqual(model.detail?.title, "My document")
37 struct Stats: Decodable { let documents: Int; let updates: [Data]; let title: String; let creations: Int; let metadata: Int }
38 let stats: Stats = try await client.request("/stats")
39 XCTAssertEqual(stats.documents, 1)
40 XCTAssertEqual(stats.creations, 2)
41 XCTAssertEqual(stats.title, "My document")
42 XCTAssertGreaterThanOrEqual(stats.metadata, 2, "Reconnection must refresh capabilities while rename is pending")
43 let peer = CRDTReplica(guid: "different-server-guid")
44 _ = try await peer.open(update: Data())
45 for update in stats.updates { _ = try await peer.apply(update) }
46 let projected = try await peer.projection()
47 XCTAssertEqual(projected.children.map(\.plainText), ["Draft text"], "Insertions and deletes converge across GUIDs")
48 await peer.close(); await model.stop()
49
50 let closing = DocsModel(client: client, draft: true)
51 await closing.newDraft()
52 closing.renameInput = String(repeating: "a", count: 1025)
53 let refused = await closing.saveAll()
54 XCTAssertFalse(refused)
55 XCTAssertNotNil(closing.renameInput, "Invalid title must remain editable")
56 closing.renameInput = "Saved on close"
57 let closed = await closing.saveAll()
58 XCTAssertTrue(closed, "Close-save must await creation and the pending title")
59 XCTAssertFalse(closing.isDraft)
60 await closing.stop()
61 let beforeDiscard: Stats = try await client.request("/stats")
62 let discarded = DocsModel(client: client, draft: true)
63 await discarded.newDraft()
64 try await Task.sleep(for: .milliseconds(100))
65 await discarded.discardAll()
66 try await Task.sleep(for: .milliseconds(1200))
67 XCTAssertNil(discarded.detail)
68 XCTAssertFalse(discarded.hasUnsavedChanges)
69 let after: Stats = try await client.request("/stats")
70 XCTAssertEqual(after.updates.count, beforeDiscard.updates.count, "Late creation must not upload a discarded draft")
71 }
72}
73