| 1 | import XCTest |
| 2 | |
| 3 | @testable import CallsKit |
| 4 | |
| 5 | /// The guard rails around starting a meeting. The request itself is Go's |
| 6 | /// (callscore.StartMeeting, gated server-side), so what is worth holding |
| 7 | /// here is the state the button is allowed to be in at all. |
| 8 | /// |
| 9 | /// Every model here has an in-memory store: none of these tests signs |
| 10 | /// in, and none should be able to touch the Keychain of whoever runs it. |
| 11 | final class StartMeetingTests: XCTestCase { |
| 12 | @MainActor func testStartingIsRefusedOnlyOnceTheServerHasSaidSo() { |
| 13 | let model = CallsModel(store: MemorySessionStore()) |
| 14 | // canStart goes down only when the server answered that this session |
| 15 | // is not a member's. Refusing here means the app does not spend a |
| 16 | // round trip learning it again. |
| 17 | model.signedIn = true |
| 18 | model.room = "abc123" |
| 19 | model.canStart = false |
| 20 | model.startMeeting() |
| 21 | XCTAssertFalse(model.starting) |
| 22 | XCTAssertFalse(model.error.isEmpty) |
| 23 | XCTAssertEqual(model.room, "abc123", "a refused start must not disturb the meeting in hand") |
| 24 | } |
| 25 | |
| 26 | @MainActor func testStartingIsRefusedDuringACallRatherThanReplacingIt() { |
| 27 | let model = CallsModel(store: MemorySessionStore()) |
| 28 | model.signedIn = true |
| 29 | model.canStart = true |
| 30 | model.joining = true |
| 31 | model.error = "" |
| 32 | model.startMeeting() |
| 33 | XCTAssertFalse(model.starting) |
| 34 | // Silent, unlike the case above: this is a button that should not have |
| 35 | // been reachable, not a person being told something they can act on. |
| 36 | XCTAssertTrue(model.error.isEmpty) |
| 37 | } |
| 38 | |
| 39 | @MainActor func testLeavingAndCancellingBothPutTheStartingFlagDown() { |
| 40 | // `starting` gates the button. Left up by a leave or a cancel it would |
| 41 | // be a button nobody can press again for the rest of the session. |
| 42 | let model = CallsModel(store: MemorySessionStore()) |
| 43 | model.starting = true |
| 44 | model.cancelSignIn() |
| 45 | XCTAssertFalse(model.starting) |
| 46 | model.starting = true |
| 47 | model.leave() |
| 48 | XCTAssertFalse(model.starting) |
| 49 | } |
| 50 | |
| 51 | @MainActor func testALinkToAnotherInstanceIsOfferedAndDisturbsNothingUntilSignIn() { |
| 52 | let model = CallsModel(store: MemorySessionStore()) |
| 53 | model.origin = "https://calls.oficina.works" |
| 54 | model.signedIn = true |
| 55 | model.canStart = true |
| 56 | model.room = "abc123" |
| 57 | model.openMeetingURL( |
| 58 | URL(string: "oficina-calls://join?url=https%3A%2F%2Fcalls.example.com%2Fj%2Finvitation")!) |
| 59 | XCTAssertEqual(model.pendingLink, "https://calls.example.com/j/invitation") |
| 60 | XCTAssertFalse(model.pendingOnServer) |
| 61 | XCTAssertFalse(model.authenticating, "offered, not started") |
| 62 | // The session in hand is untouched until a new sign-in replaces it, |
| 63 | // and "Not now" goes straight back to it. |
| 64 | XCTAssertTrue(model.signedIn) |
| 65 | XCTAssertTrue(model.canStart) |
| 66 | XCTAssertEqual(model.room, "abc123") |
| 67 | model.dismissPendingLink() |
| 68 | XCTAssertEqual(model.pendingLink, "") |
| 69 | XCTAssertTrue(model.signedIn) |
| 70 | } |
| 71 | |
| 72 | @MainActor func testHostIsTheInstanceWithoutItsScheme() { |
| 73 | let model = CallsModel(store: MemorySessionStore()) |
| 74 | model.origin = "https://calls.oficina.works" |
| 75 | XCTAssertEqual(model.host, "calls.oficina.works") |
| 76 | model.origin = "http://127.0.0.1:8080" |
| 77 | XCTAssertEqual(model.host, "127.0.0.1") |
| 78 | } |
| 79 | } |
| 80 | |