From f08444b25c43f4f93edbc41e0ed77ae1c4812860 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 12 Jan 2026 12:29:11 -0800 Subject: [PATCH 1/3] [url_launcher_ios] Migrate XCTest to Swift Testing --- .../url_launcher_ios/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 +- .../ios/RunnerTests/URLLauncherTests.swift | 189 ++++++++---------- 3 files changed, 93 insertions(+), 104 deletions(-) diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index bf1fef3950b0..828c19b3c28c 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Migrates unit tests to Swift Testing. + ## 6.4.0 * Improves compatibility with `UIScene`. diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj index 6afbb72dc9a9..940a0bc72539 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -650,7 +650,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Debug; @@ -671,7 +671,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Release; diff --git a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift index 30cc5903559c..718c980d442b 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift +++ b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift @@ -3,7 +3,7 @@ // found in the LICENSE file. import Flutter -import XCTest +import Testing @testable import url_launcher_ios @@ -31,7 +31,8 @@ final class StubViewPresenterProvider: ViewPresenterProvider { } } -final class URLLauncherTests: XCTestCase { +@MainActor +struct URLLauncherTests { private func createPlugin( launcher: FakeLauncher = FakeLauncher(), viewPresenter: ViewPresenter? = TestViewPresenter() @@ -41,147 +42,131 @@ final class URLLauncherTests: XCTestCase { viewPresenterProvider: StubViewPresenterProvider(viewPresenter: viewPresenter)) } - func testCanLaunchSuccess() { - let result = createPlugin().canLaunchUrl(url: "good://url") - XCTAssertEqual(result, .success) + @Test(arguments: [ + ("good://url", LaunchResult.success), + ("bad://url", .failure), + ]) + func canLaunch(url: String, expected: LaunchResult) { + let result = createPlugin().canLaunchUrl(url: url) + #expect(result == expected) } - func testCanLaunchFailure() { - let result = createPlugin().canLaunchUrl(url: "bad://url") - XCTAssertEqual(result, .failure) - } - - func testCanLaunchFailureWithInvalidURL() { + @Test func canLaunchFailureWithInvalidURL() { let result = createPlugin().canLaunchUrl(url: "urls can't have spaces") - if urlParsingIsStrict() { - XCTAssertEqual(result, .invalidUrl) + #expect(result == .invalidUrl) } else { - XCTAssertEqual(result, .failure) + #expect(result == .failure) } } - func testLaunchSuccess() { - let expectation = XCTestExpectation(description: "completion called") - createPlugin().launchUrl(url: "good://url", universalLinksOnly: false) { result in - switch result { - case .success(let details): - XCTAssertEqual(details, .success) - case .failure(let error): - XCTFail("Unexpected error: \(error)") - } - expectation.fulfill() - } - - wait(for: [expectation], timeout: 1) - } - - func testLaunchFailure() { - let expectation = XCTestExpectation(description: "completion called") - createPlugin().launchUrl(url: "bad://url", universalLinksOnly: false) { result in - switch result { - case .success(let details): - XCTAssertEqual(details, .failure) - case .failure(let error): - XCTFail("Unexpected error: \(error)") + @Test(arguments: [ + ("good://url", LaunchResult.success), + ("bad://url", .failure), + ]) + func launch(url: String, expected: LaunchResult) async { + await confirmation("completion called") { confirmed in + createPlugin().launchUrl(url: url, universalLinksOnly: false) { result in + switch result { + case .success(let details): + #expect(details == expected) + case .failure(let error): + Issue.record("Unexpected error: \(error)") + } + confirmed() } - expectation.fulfill() } - - wait(for: [expectation], timeout: 1) } - func testLaunchFailureWithInvalidURL() { - let expectation = XCTestExpectation(description: "completion called") - createPlugin().launchUrl(url: "urls can't have spaces", universalLinksOnly: false) { result in - switch result { - case .success(let details): - if urlParsingIsStrict() { - XCTAssertEqual(details, .invalidUrl) - } else { - XCTAssertEqual(details, .failure) + @Test func launchFailureWithInvalidURL() async { + await confirmation("completion called") { confirmed in + createPlugin().launchUrl(url: "urls can't have spaces", universalLinksOnly: false) { result in + switch result { + case .success(let details): + if urlParsingIsStrict() { + #expect(details == .invalidUrl) + } else { + #expect(details == .failure) + } + case .failure(let error): + Issue.record("Unexpected error: \(error)") } - case .failure(let error): - XCTFail("Unexpected error: \(error)") + confirmed() } - expectation.fulfill() } - - wait(for: [expectation], timeout: 1) } - func testLaunchWithoutUniversalLinks() { + @Test func launchWithoutUniversalLinks() async throws { let launcher = FakeLauncher() let plugin = createPlugin(launcher: launcher) - let expectation = XCTestExpectation(description: "completion called") - plugin.launchUrl(url: "good://url", universalLinksOnly: false) { result in - switch result { - case .success(let details): - XCTAssertEqual(details, .success) - case .failure(let error): - XCTFail("Unexpected error: \(error)") + await confirmation("completion called") { confirmed in + plugin.launchUrl(url: "good://url", universalLinksOnly: false) { result in + switch result { + case .success(let details): + #expect(details == .success) + case .failure(let error): + Issue.record("Unexpected error: \(error)") + } + confirmed() } - expectation.fulfill() } - - wait(for: [expectation], timeout: 1) - XCTAssertEqual(launcher.passedOptions?[.universalLinksOnly] as? Bool, false) + let passedOptions = try #require(launcher.passedOptions) + #expect(passedOptions[.universalLinksOnly] as? Bool == false) } - func testLaunchWithUniversalLinks() { + @Test func launchWithUniversalLinks() async throws { let launcher = FakeLauncher() let plugin = createPlugin(launcher: launcher) - let expectation = XCTestExpectation(description: "completion called") - plugin.launchUrl(url: "good://url", universalLinksOnly: true) { result in - switch result { - case .success(let details): - XCTAssertEqual(details, .success) - case .failure(let error): - XCTFail("Unexpected error: \(error)") + await confirmation("completion called") { confirmed in + plugin.launchUrl(url: "good://url", universalLinksOnly: true) { result in + switch result { + case .success(let details): + #expect(details == .success) + case .failure(let error): + Issue.record("Unexpected error: \(error)") + } + confirmed() } - expectation.fulfill() } - - wait(for: [expectation], timeout: 1) - XCTAssertEqual(launcher.passedOptions?[.universalLinksOnly] as? Bool, true) + let passedOptions = try #require(launcher.passedOptions) + #expect(passedOptions[.universalLinksOnly] as? Bool == true) } - func testLaunchSafariViewControllerWithClose() { + @Test func launchSafariViewControllerWithClose() async throws { let launcher = FakeLauncher() let viewPresenter = TestViewPresenter() let plugin = createPlugin(launcher: launcher, viewPresenter: viewPresenter) - let expectation = XCTestExpectation(description: "completion called") - plugin.openUrlInSafariViewController(url: "https://flutter.dev") { result in - switch result { - case .success(let details): - XCTAssertEqual(details, .dismissed) - case .failure(let error): - XCTFail("Unexpected error: \(error)") + await confirmation("completion called") { confirmed in + plugin.openUrlInSafariViewController(url: "https://flutter.dev") { result in + switch result { + case .success(let details): + #expect(details == .dismissed) + case .failure(let error): + Issue.record("Unexpected error: \(error)") + } + confirmed() } - expectation.fulfill() + plugin.closeSafariViewController() } - plugin.closeSafariViewController() - wait(for: [expectation], timeout: 30) - XCTAssertNotNil(viewPresenter.presentedController) + #expect(viewPresenter.presentedController != nil) } - func testLaunchSafariViewControllerFailureWithNoViewPresenter() { - let expectation = XCTestExpectation(description: "completion called") - createPlugin(viewPresenter: nil).openUrlInSafariViewController(url: "https://flutter.dev") { - result in - switch result { - case .success(let details): - XCTAssertEqual(details, .noUI) - case .failure(let error): - XCTFail("Unexpected error: \(error)") + @Test func launchSafariViewControllerFailureWithNoViewPresenter() async { + await confirmation("completion called") { confirmed in + createPlugin(viewPresenter: nil).openUrlInSafariViewController(url: "https://flutter.dev") { + result in + switch result { + case .success(let details): + #expect(details == .noUI) + case .failure(let error): + Issue.record("Unexpected error: \(error)") + } + confirmed() } - expectation.fulfill() } - - wait(for: [expectation], timeout: 1) } } From ca1db26547ab2493c1f38e9633425add6d30761d Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 12 Jan 2026 12:47:37 -0800 Subject: [PATCH 2/3] Remove extra throws Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../example/ios/RunnerTests/URLLauncherTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift index 718c980d442b..d23c636454cd 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift +++ b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.swift @@ -134,7 +134,7 @@ struct URLLauncherTests { #expect(passedOptions[.universalLinksOnly] as? Bool == true) } - @Test func launchSafariViewControllerWithClose() async throws { + @Test func launchSafariViewControllerWithClose() async { let launcher = FakeLauncher() let viewPresenter = TestViewPresenter() let plugin = createPlugin(launcher: launcher, viewPresenter: viewPresenter) From 430e50b8c297c64e73350fc50e128e42ca813510 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Tue, 13 Jan 2026 21:59:19 -0800 Subject: [PATCH 3/3] Remove CHANGELOG.md --- packages/url_launcher/url_launcher_ios/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 828c19b3c28c..bf1fef3950b0 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,7 +1,3 @@ -## NEXT - -* Migrates unit tests to Swift Testing. - ## 6.4.0 * Improves compatibility with `UIScene`.