diff --git a/Coder-Desktop/Coder-Desktop/Views/VPN/VPNMenu.swift b/Coder-Desktop/Coder-Desktop/Views/VPN/VPNMenu.swift index 2a9e2254..a48be35f 100644 --- a/Coder-Desktop/Coder-Desktop/Views/VPN/VPNMenu.swift +++ b/Coder-Desktop/Coder-Desktop/Views/VPN/VPNMenu.swift @@ -117,12 +117,15 @@ struct VPNMenu: View { } private var vpnDisabled: Bool { - vpn.state == .connecting || - vpn.state == .disconnecting || - // Prevent starting the VPN before the user has approved the system extension. - vpn.state == .failed(.systemExtensionError(.needsUserApproval)) || - // Prevent starting the VPN without a VPN configuration. - vpn.state == .failed(.networkExtensionError(.unconfigured)) + // Always enabled if signed out, as that will open the sign in window + state.hasSession && ( + vpn.state == .connecting || + vpn.state == .disconnecting || + // Prevent starting the VPN before the user has approved the system extension. + vpn.state == .failed(.systemExtensionError(.needsUserApproval)) || + // Prevent starting the VPN without a VPN configuration. + vpn.state == .failed(.networkExtensionError(.unconfigured)) + ) } } diff --git a/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift b/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift index 46c780ca..322952bf 100644 --- a/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift +++ b/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift @@ -32,6 +32,21 @@ struct VPNMenuTests { } } + @Test + func testVPNLoggedOutUnconfigured() async throws { + vpn.state = .failed(.networkExtensionError(.unconfigured)) + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in + let toggle = try view.find(ViewType.Toggle.self) + // Toggle should be enabled even with a failure that would + // normally make it disabled, because we're signed out. + #expect(!toggle.isDisabled()) + #expect(throws: Never.self) { try view.find(text: "Sign in to use Coder Desktop") } + #expect(throws: Never.self) { try view.find(button: "Sign in") } + } + } + } + @Test func testStartStopCalled() async throws { try await ViewHosting.host(view) { @@ -59,6 +74,7 @@ struct VPNMenuTests { @Test func testVPNDisabledWhileConnecting() async throws { vpn.state = .disabled + state.login(baseAccessURL: URL(string: "https://coder.example.com")!, sessionToken: "fake-token") try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -79,6 +95,7 @@ struct VPNMenuTests { @Test func testVPNDisabledWhileDisconnecting() async throws { vpn.state = .disabled + state.login(baseAccessURL: URL(string: "https://coder.example.com")!, sessionToken: "fake-token") try await ViewHosting.host(view) { try await sut.inspection.inspect { view in