1 import ExpoModulesTestCore 2 3 @testable import ExpoModulesCore 4 5 class PropertyComponentSpec: ExpoSpec { specnull6 override func spec() { 7 describe("property") { 8 let appContext = AppContext.create() 9 let runtime = try! appContext.runtime 10 11 it("gets the value") { 12 let property = Property("test") { return "expo" } 13 expect(try property.getValue(appContext: appContext)) == "expo" 14 } 15 16 it("sets the value") { 17 var value = Int.random(in: 0..<100) 18 let property = Property("test") 19 .get { value } 20 .set { (newValue: Int) in 21 value = newValue 22 } 23 24 let newValue = Int.random(in: 0..<100) 25 property.setValue(newValue, appContext: appContext) 26 27 expect(try property.getValue(appContext: appContext)) == value 28 expect(value) == newValue 29 } 30 } 31 32 describe("module property") { 33 let appContext = AppContext.create() 34 let runtime = try! appContext.runtime 35 36 beforeSuite { 37 appContext.moduleRegistry.register(moduleType: PropertyTestModule.self) 38 } 39 40 it("gets read-only property") { 41 let value = try runtime.eval("expo.modules.PropertyTest.readOnly") 42 expect(value.getString()) == "foo" 43 } 44 45 it("gets writable property") { 46 let value = try runtime.eval("expo.modules.PropertyTest.writable") 47 expect(value.getInt()) == 444 48 } 49 50 it("sets writable property") { 51 try runtime.eval("expo.modules.PropertyTest.writable = 777") 52 let value = try runtime.eval("expo.modules.PropertyTest.writable") 53 expect(value.getInt()) == 777 54 } 55 56 it("is enumerable") { 57 let keys = try runtime.eval("Object.keys(expo.modules.PropertyTest)").getArray().map { $0.getString() } ?? [] 58 expect(keys).to(contain("readOnly", "writable", "undefined")) 59 } 60 61 // TODO: Using JavaScriptObject as the owner is no longer possible, but we may want to bring this feature back 62 // it("is called with the caller") { 63 // let value = try runtime?.eval("expo.modules.PropertyTest.withCaller") 64 // expect(value?.getString()) == "foo" 65 // } 66 67 it("returns undefined when getter is not specified") { 68 let value = try runtime.eval("expo.modules.PropertyTest.undefined") 69 expect(value.isUndefined()) == true 70 } 71 } 72 73 describe("class property") { 74 let appContext = AppContext.create() 75 let runtime = try! appContext.runtime 76 77 beforeSuite { 78 appContext.moduleRegistry.register(moduleType: PropertyTestModule.self) 79 } 80 81 it("gets the value") { 82 let value = try runtime.eval("new expo.modules.PropertyTest.TestClass().someValue") 83 84 expect(value.kind) == .number 85 expect(value.getInt()) == TestClass.constantValue 86 } 87 88 it("sets the value") { 89 let newValue = Int.random(in: 1..<100) 90 let value = try runtime.eval([ 91 "object = new expo.modules.PropertyTest.TestClass()", 92 "object.someValue = \(newValue)", 93 "object.someValue" 94 ]) 95 96 expect(value.kind) == .number 97 expect(value.getInt()) == newValue 98 } 99 100 // Tests for accessing shared object properties through KeyPath and ReferenceWritableKeyPath 101 describe("key path") { 102 it("gets immutable property") { 103 let value = try runtime.eval([ 104 "object = new expo.modules.PropertyTest.TestClass()", 105 "object.immutableKeyPathProperty" 106 ]) 107 108 expect(value.kind) == .number 109 expect(value.getInt()) == TestClass.constantValue 110 } 111 112 it("cannot set immutable property") { 113 let newValue = Int.random(in: 100..<200) 114 let value = try runtime.eval([ 115 "object = new expo.modules.PropertyTest.TestClass()", 116 "object.immutableKeyPathProperty = \(newValue)", 117 "object.immutableKeyPathProperty" 118 ]) 119 120 // Returned value didn't change, it doesn't equal to `newValue` 121 expect(value.kind) == .number 122 expect(value.getInt()) == TestClass.constantValue 123 } 124 125 it("sets mutable property") { 126 let newValue = Int.random(in: 100..<200) 127 let value = try runtime.eval([ 128 "object = new expo.modules.PropertyTest.TestClass()", 129 "object.mutableKeyPathProperty = \(newValue)", 130 "object.mutableKeyPathProperty" 131 ]) 132 133 expect(value.kind) == .number 134 expect(value.getInt()) == newValue 135 } 136 } 137 } 138 } 139 } 140 141 class PropertyTestModule: Module { definitionnull142 func definition() -> ModuleDefinition { 143 Name("PropertyTest") 144 145 Property("readOnly") { 146 return "foo" 147 } 148 149 var writablePropertyValue = 444 150 Property("writable") 151 .get { 152 return writablePropertyValue 153 } 154 .set { value in 155 writablePropertyValue = value 156 } 157 158 // TODO: Using JavaScriptObject as the owner is no longer possible, but we may want to bring this feature back 159 // Property("withCaller") { (caller: JavaScriptObject) -> String in 160 // // Here, the caller is a JS object of the module. 161 // // Return another property of itself. 162 // return caller.getProperty("readOnly").getString() 163 // } 164 165 Property("undefined") 166 167 Class(TestClass.self) { 168 Constructor { 169 return TestClass() 170 } 171 172 Property("someValue") { object in 173 return object.someValue 174 } 175 .set { object, newValue in 176 object.someValue = newValue 177 } 178 179 // KeyPath<TestClass, Int> 180 Property("immutableKeyPathProperty", \.immutableKeyPathProperty) 181 182 // ReferenceWritableKeyPath<TestClass, Int> 183 Property("mutableKeyPathProperty", \.mutableKeyPathProperty) 184 } 185 } 186 } 187 188 fileprivate final class TestClass: SharedObject { 189 static let constantValue = Int.random(in: 1..<100) 190 191 var someValue = TestClass.constantValue 192 193 // For "key path" tests 194 let immutableKeyPathProperty = TestClass.constantValue 195 var mutableKeyPathProperty = TestClass.constantValue 196 } 197