diff --git a/McBopomofoTests/PreferencesTests.swift b/McBopomofoTests/PreferencesTests.swift index 905f827c..6c4ebc56 100644 --- a/McBopomofoTests/PreferencesTests.swift +++ b/McBopomofoTests/PreferencesTests.swift @@ -156,3 +156,102 @@ class PreferencesTests: XCTestCase { } } + +class CandidateKeyValidationTests: XCTestCase { + func testEmpty() { + do { + try Preferences.validate(candidateKeys: "") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.empty) { + } catch { + XCTFail("exception not thrown") + } + } + + func testSpaces() { + do { + try Preferences.validate(candidateKeys: " ") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.empty) { + } catch { + XCTFail("exception not thrown") + } + } + + func testInvalidKeys() { + do { + try Preferences.validate(candidateKeys: "中文字元") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.invalidCharacters) { + } catch { + XCTFail("exception not thrown") + } + } + + func testInvalidLatinLetters() { + do { + try Preferences.validate(candidateKeys: "üåçøöacpo") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.invalidCharacters) { + } catch { + XCTFail("exception not thrown") + } + } + + func testSpaceInBetween() { + do { + try Preferences.validate(candidateKeys: "1 2 3 4") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.containSpace) { + } catch { + XCTFail("exception not thrown") + } + } + + func testDuplicatedKeys() { + do { + try Preferences.validate(candidateKeys: "aabbccdd") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.duplicatedCharacters) { + } catch { + XCTFail("exception not thrown") + } + } + + func testTooShort1() { + do { + try Preferences.validate(candidateKeys: "abc") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.tooShort) { + } catch { + XCTFail("exception not thrown") + } + } + + func testTooShort2() { + do { + try Preferences.validate(candidateKeys: "abcd") + } catch { + XCTFail("Should be safe") + } + } + + func testTooLong1() { + do { + try Preferences.validate(candidateKeys: "qwertyuiopasdfgh") + XCTFail("exception not thrown") + } catch(Preferences.CandidateKeyError.tooLong) { + } catch { + XCTFail("exception not thrown") + } + } + + func testTooLong2() { + do { + try Preferences.validate(candidateKeys: "qwertyuiopasdfg") + } + catch { + XCTFail("Should be safe") + } + } +} diff --git a/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift b/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift index c583b9bb..cc287ea6 100644 --- a/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift +++ b/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift @@ -130,7 +130,7 @@ public class CandidateController: NSWindowController { // bottom beneath the screen? if adjustedPoint.y - windowSize.height < screenFrame.minY { - adjustedPoint.y = windowTopLeftPoint.y + adjustedHeight + windowSize.height + adjustedPoint.y = screenFrame.minY + adjustedHeight + windowSize.height } // top over the screen? diff --git a/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift b/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift index 1aa3003d..585355aa 100644 --- a/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift +++ b/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift @@ -19,6 +19,35 @@ class HorizontalCandidateControllerTests: XCTestCase { } } + func testPositioning1() { + let controller = HorizontalCandidateController() + let mock = Mock() + controller.delegate = mock + controller.keyLabels = ["1", "2", "3", "4"] + controller.reloadData() + controller.visible = true + controller.set(windowTopLeftPoint: NSPoint(x: -100, y: -100), bottomOutOfScreenAdjustmentHeight: 10) + let exp = expectation(description: "wait") + _ = XCTWaiter.wait(for: [exp], timeout: 0.2) + XCTAssert (controller.window?.frame.minX ?? -1 >= 0) + XCTAssert (controller.window?.frame.minY ?? -1 >= 0) + } + + func testPositioning2() { + let controller = HorizontalCandidateController() + let mock = Mock() + controller.delegate = mock + controller.keyLabels = ["1", "2", "3", "4"] + controller.reloadData() + controller.visible = true + let screenRect = NSScreen.main?.frame ?? NSRect.zero + controller.set(windowTopLeftPoint: NSPoint(x: screenRect.maxX + 100, y: screenRect.maxY + 100), bottomOutOfScreenAdjustmentHeight: 10) + let exp = expectation(description: "wait") + _ = XCTWaiter.wait(for: [exp], timeout: 0.2) + XCTAssert (controller.window?.frame.maxX ?? CGFloat.greatestFiniteMagnitude <= screenRect.maxX) + XCTAssert (controller.window?.frame.maxY ?? CGFloat.greatestFiniteMagnitude <= screenRect.maxY ) + } + func testReloadData() { let controller = HorizontalCandidateController() let mock = Mock() diff --git a/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift b/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift index afc54c41..0f11378c 100644 --- a/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift +++ b/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift @@ -19,6 +19,35 @@ class VerticalCandidateControllerTests: XCTestCase { } } + func testPositioning1() { + let controller = HorizontalCandidateController() + let mock = Mock() + controller.delegate = mock + controller.keyLabels = ["1", "2", "3", "4"] + controller.reloadData() + controller.visible = true + controller.set(windowTopLeftPoint: NSPoint(x: -100, y: -100), bottomOutOfScreenAdjustmentHeight: 10) + let exp = expectation(description: "wait") + _ = XCTWaiter.wait(for: [exp], timeout: 0.2) + XCTAssert (controller.window?.frame.minX ?? -1 >= 0) + XCTAssert (controller.window?.frame.minY ?? -1 >= 0) + } + + func testPositioning2() { + let controller = HorizontalCandidateController() + let mock = Mock() + controller.delegate = mock + controller.keyLabels = ["1", "2", "3", "4"] + controller.reloadData() + controller.visible = true + let screenRect = NSScreen.main?.frame ?? NSRect.zero + controller.set(windowTopLeftPoint: NSPoint(x: screenRect.maxX + 100, y: screenRect.maxY + 100), bottomOutOfScreenAdjustmentHeight: 10) + let exp = expectation(description: "wait") + _ = XCTWaiter.wait(for: [exp], timeout: 0.2) + XCTAssert (controller.window?.frame.maxX ?? CGFloat.greatestFiniteMagnitude <= screenRect.maxX) + XCTAssert (controller.window?.frame.maxY ?? CGFloat.greatestFiniteMagnitude <= screenRect.maxY ) + } + func testReloadData() { let controller = VerticalCandidateController() let mock = Mock()