Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 43 additions & 12 deletions ios/App/AppUITests/MoveDirectoryUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ final class MoveDirectoryUITests: XCTestCase {
try createFolder(named: source, in: app)
try createFolder(named: destination, in: app)

// 1. Long-press the source row: the sheet has Move to…, and the core's
// prompt takes an inbox-relative destination path.
// 1. Long-press the source row: the sheet has Move to…, and since core
// 2.55.0 (zennotes#844) the core's prompt spells destinations the
// way the sidebar does: a folder of the notes area by its own path,
// with no inbox/ in front, so the same text works on an Inbox vault
// and on one whose notes live at the vault root.
let sourceRow = try row(labelPrefix: source, in: app)
sourceRow.press(forDuration: 0.8)
let move = try button(label: "Move to…", in: app, failure: "the folder sheet has no Move to… row")
attachScreenshot("folder sheet", of: app)
move.tap()

let target = promptField(placeholder: "inbox/Work", in: app)
// Touch prompts with suggestions are tap-first (no autofocus), so the
// field needs a tap before typing; the typed path filters the list and
// Move submits the typed value.
let target = promptField(placeholderSuffix: "(type a folder to change)", in: app)
// The typed path filters the list and Move submits the typed value.
waitUntilHittable(target, failure: "the Move prompt did not open")
target.tap()
app.typeText("inbox/\(destination)")
focusPromptField(target)
app.typeText(destination)
attachScreenshot("move prompt", of: app)
try button(label: "Move", in: app, failure: "the Move prompt has no Move button").tap()

Expand Down Expand Up @@ -82,14 +83,12 @@ final class MoveDirectoryUITests: XCTestCase {
try hittable(label: "Browse", in: app, failure: "the ensō menu did not open").tap()
}

/// "New folder" at the drawer's current directory. A tap on the field is
/// harmless when the prompt already focused it, and needed on a simulator
/// whose hardware keyboard kept the software one down.
/// "New folder" at the drawer's current directory.
private func createFolder(named name: String, in app: XCUIApplication) throws {
try row(labelPrefix: "New folder", in: app, failure: "no New folder row in Browse").tap()
let field = promptField(placeholder: "Folder name", in: app)
waitUntilHittable(field, failure: "the New folder prompt did not open")
field.tap()
focusPromptField(field)
app.typeText(name)
try button(label: "Create", in: app, failure: "the New folder prompt has no Create button").tap()
_ = try row(labelPrefix: name, in: app, failure: "created folder \(name) did not appear in Browse")
Expand All @@ -103,6 +102,38 @@ final class MoveDirectoryUITests: XCTestCase {
.firstMatch
}

/// The Move prompt's placeholder opens with the notes root's name (the
/// Inbox folder's, or "Vault root"), which depends on the vault, so only
/// its fixed ending identifies the field.
private func promptField(placeholderSuffix suffix: String, in app: XCUIApplication) -> XCUIElement {
app.textFields
.matching(NSPredicate(format: "placeholderValue ENDSWITH %@", suffix))
.firstMatch
}

/// A prompt's field may arrive focused or not. PromptModal keeps touch
/// pickers tap-first, but with core 2.52.0 to 2.60.0 the dialog shell
/// (useDialogFocus) focused a panel's first control on open, so the Move
/// prompt raised the keyboard itself and its sheet rose with it. A
/// tap aimed at the field's frame from before that rise blurred the field
/// again, and typing then had no target. So the field gets time to take
/// focus on its own, and a tap only when it did not (a tap-first prompt,
/// or a hardware keyboard keeping the software one down).
private func focusPromptField(_ field: XCUIElement, timeout: TimeInterval = 2) {
if waitForKeyboardFocus(field, timeout: timeout) { return }
field.tap()
XCTAssertTrue(waitForKeyboardFocus(field, timeout: 5), "the prompt field did not take keyboard focus")
}

private func waitForKeyboardFocus(_ element: XCUIElement, timeout: TimeInterval) -> Bool {
let deadline = Date().addingTimeInterval(timeout)
repeat {
if (element.value(forKey: "hasKeyboardFocus") as? Bool) == true { return true }
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
} while Date() < deadline
return false
}

/// Menus, sheets and dialogs animate in, and an element reports its final
/// frame before it can take a tap there.
private func waitUntilHittable(_ element: XCUIElement, timeout: TimeInterval = 10, failure: String) {
Expand Down
Loading