tauri-plugin-widgets 0.4.2

Tauri plugin for App Widgets on Android, iOS, and macOS (WidgetKit); Windows Widgets Board (Adaptive Cards) + desktop webview; Linux desktop webview with X11 DESKTOP pin.
import XCTest

@testable import tauri_plugin_widgets

// MARK: - Decodable Args Tests

final class DecodableArgsTests: XCTestCase {

    // MARK: SetItemsArgs

    func testSetItemsArgsDecoding() throws {
        let json = """
        {"key": "widget_text", "value": "Hello", "group": "group.com.example"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(SetItemsArgs.self, from: json)
        XCTAssertEqual(args.key, "widget_text")
        XCTAssertEqual(args.value, "Hello")
        XCTAssertEqual(args.group, "group.com.example")
    }

    func testSetItemsArgsDecodingWithEmptyValue() throws {
        let json = """
        {"key": "k", "value": "", "group": "g"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(SetItemsArgs.self, from: json)
        XCTAssertEqual(args.value, "")
    }

    func testSetItemsArgsMissingFieldThrows() {
        let json = """
        {"key": "k", "value": "v"}
        """.data(using: .utf8)!

        XCTAssertThrowsError(try JSONDecoder().decode(SetItemsArgs.self, from: json))
    }

    // MARK: GetItemsArgs

    func testGetItemsArgsDecoding() throws {
        let json = """
        {"key": "title", "group": "group.app"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(GetItemsArgs.self, from: json)
        XCTAssertEqual(args.key, "title")
        XCTAssertEqual(args.group, "group.app")
    }

    func testGetItemsArgsMissingKeyThrows() {
        let json = """
        {"group": "g"}
        """.data(using: .utf8)!

        XCTAssertThrowsError(try JSONDecoder().decode(GetItemsArgs.self, from: json))
    }

    // MARK: SetRegisterWidgetArgs

    func testSetRegisterWidgetArgsDecoding() throws {
        let json = """
        {"widgets": ["com.example.Widget1", "com.example.Widget2"]}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(SetRegisterWidgetArgs.self, from: json)
        XCTAssertEqual(args.widgets.count, 2)
        XCTAssertEqual(args.widgets[0], "com.example.Widget1")
        XCTAssertEqual(args.widgets[1], "com.example.Widget2")
    }

    func testSetRegisterWidgetArgsEmptyArray() throws {
        let json = """
        {"widgets": []}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(SetRegisterWidgetArgs.self, from: json)
        XCTAssertTrue(args.widgets.isEmpty)
    }

    // MARK: ReloadTimelinesArgs

    func testReloadTimelinesArgsDecoding() throws {
        let json = """
        {"ofKind": "MyWidgetKind"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(ReloadTimelinesArgs.self, from: json)
        XCTAssertEqual(args.ofKind, "MyWidgetKind")
    }

    func testReloadTimelinesArgsCamelCase() throws {
        let json = """
        {"ofKind": "com.example.MyWidget"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(ReloadTimelinesArgs.self, from: json)
        XCTAssertEqual(args.ofKind, "com.example.MyWidget")
    }

    func testReloadTimelinesArgsSnakeCaseFails() {
        let json = """
        {"of_kind": "test"}
        """.data(using: .utf8)!

        XCTAssertThrowsError(try JSONDecoder().decode(ReloadTimelinesArgs.self, from: json))
    }
}

// MARK: - UserDefaults Integration Tests

final class UserDefaultsIntegrationTests: XCTestCase {

    private let testSuite = "test.tauri.plugin.widgets.\(UUID().uuidString)"

    override func tearDown() {
        super.tearDown()
        UserDefaults.standard.removePersistentDomain(forName: testSuite)
    }

    func testSetAndGetString() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        defaults.set("Hello World", forKey: "widget_text")
        defaults.synchronize()

        let value = defaults.string(forKey: "widget_text")
        XCTAssertEqual(value, "Hello World")
    }

    func testGetMissingKeyReturnsNil() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        let value = defaults.string(forKey: "nonexistent_key")
        XCTAssertNil(value)
    }

    func testOverwriteValue() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        defaults.set("first", forKey: "key")
        defaults.synchronize()
        XCTAssertEqual(defaults.string(forKey: "key"), "first")

        defaults.set("second", forKey: "key")
        defaults.synchronize()
        XCTAssertEqual(defaults.string(forKey: "key"), "second")
    }

    func testMultipleKeys() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        defaults.set("val1", forKey: "key1")
        defaults.set("val2", forKey: "key2")
        defaults.set("val3", forKey: "key3")
        defaults.synchronize()

        XCTAssertEqual(defaults.string(forKey: "key1"), "val1")
        XCTAssertEqual(defaults.string(forKey: "key2"), "val2")
        XCTAssertEqual(defaults.string(forKey: "key3"), "val3")
    }

    func testEmptyStringValue() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        defaults.set("", forKey: "empty_key")
        defaults.synchronize()

        let value = defaults.string(forKey: "empty_key")
        XCTAssertEqual(value, "")
    }

    func testJsonStringValue() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        let jsonString = "{\"title\":\"Hello\",\"count\":42}"
        defaults.set(jsonString, forKey: "json_data")
        defaults.synchronize()

        let value = defaults.string(forKey: "json_data")
        XCTAssertEqual(value, jsonString)
    }

    func testUnicodeValue() {
        guard let defaults = UserDefaults(suiteName: testSuite) else {
            XCTFail("Could not create UserDefaults for suite: \(testSuite)")
            return
        }

        let unicodeStr = "Привет мир 你好世界 🌍"
        defaults.set(unicodeStr, forKey: "unicode_key")
        defaults.synchronize()

        let value = defaults.string(forKey: "unicode_key")
        XCTAssertEqual(value, unicodeStr)
    }
}

// MARK: - Plugin Init Tests

final class PluginInitTests: XCTestCase {

    func testInitPluginReturnsWidgetPlugin() {
        let plugin = initPlugin()
        XCTAssertTrue(plugin is WidgetPlugin)
    }

    func testWidgetPluginCreation() {
        let plugin = WidgetPlugin()
        XCTAssertNotNil(plugin)
    }
}

// MARK: - Serialization Round-Trip Tests

final class SerializationRoundTripTests: XCTestCase {

    func testSetItemsRoundTrip() throws {
        let original: [String: Any] = [
            "key": "widget_text",
            "value": "Test Value",
            "group": "group.com.example"
        ]
        let data = try JSONSerialization.data(withJSONObject: original)
        let decoded = try JSONDecoder().decode(SetItemsArgs.self, from: data)

        XCTAssertEqual(decoded.key, "widget_text")
        XCTAssertEqual(decoded.value, "Test Value")
        XCTAssertEqual(decoded.group, "group.com.example")
    }

    func testReloadTimelinesRoundTripCamelCase() throws {
        let original: [String: Any] = ["ofKind": "MyWidget"]
        let data = try JSONSerialization.data(withJSONObject: original)
        let decoded = try JSONDecoder().decode(ReloadTimelinesArgs.self, from: data)

        XCTAssertEqual(decoded.ofKind, "MyWidget")
    }

    func testSetRegisterWidgetRoundTrip() throws {
        let widgets = ["com.a.W1", "com.b.W2", "com.c.W3"]
        let original: [String: Any] = ["widgets": widgets]
        let data = try JSONSerialization.data(withJSONObject: original)
        let decoded = try JSONDecoder().decode(SetRegisterWidgetArgs.self, from: data)

        XCTAssertEqual(decoded.widgets, widgets)
    }

    func testSpecialCharactersInValues() throws {
        let json = """
        {"key": "k/e.y-1", "value": "line1\\nline2\\ttab", "group": "group.test"}
        """.data(using: .utf8)!

        let args = try JSONDecoder().decode(SetItemsArgs.self, from: json)
        XCTAssertEqual(args.key, "k/e.y-1")
        XCTAssertEqual(args.value, "line1\nline2\ttab")
    }

    func testLongValues() throws {
        let longValue = String(repeating: "A", count: 10_000)
        let original: [String: Any] = [
            "key": "big",
            "value": longValue,
            "group": "g"
        ]
        let data = try JSONSerialization.data(withJSONObject: original)
        let decoded = try JSONDecoder().decode(SetItemsArgs.self, from: data)

        XCTAssertEqual(decoded.value.count, 10_000)
    }
}