@rdlabo/capacitor-brotherprint 8.2.2 → 8.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -289,7 +289,7 @@ These are optional. If these are not set, default values are assigned by the pri
289
289
 
290
290
  #### BRLMSearchOption
291
291
 
292
- <code>{ /** * 'usb' is android only, and now developing. */ port: <a href="#brlmprinterport">BRLMPrinterPort</a>; /** * searchDuration is the time to end search for devices. * default is 15 seconds. * use only port is 'wifi' or 'bluetoothLowEnergy'. */ searchDuration: number; /** * Android Bluetooth Classic only. Include only devices whose Bluetooth class * reports a printer. Defaults to false; ignored for other ports and on iOS. * This does not identify Brother devices. Devices with an unknown class are excluded when true. */ bluetoothPrintersOnly?: boolean; }</code>
292
+ <code>{ /** * 'usb' is android only, and now developing. */ port: <a href="#brlmprinterport">BRLMPrinterPort</a>; /** * searchDuration is the time to end search for devices. * default is 15 seconds. * Used for Wi-Fi, BLE, and the iOS Bluetooth accessory picker wait. * Timing out does not dismiss the system accessory picker; close it before retrying. */ searchDuration: number; /** * Android Bluetooth Classic only. Include only devices whose Bluetooth class * reports a printer. Defaults to false; ignored for other ports and on iOS. * This does not identify Brother devices. Devices with an unknown class are excluded when true. */ bluetoothPrintersOnly?: boolean; }</code>
293
293
 
294
294
 
295
295
  #### isChannelAvailableResult
package/dist/docs.json CHANGED
@@ -929,7 +929,7 @@
929
929
  "docs": "",
930
930
  "types": [
931
931
  {
932
- "text": "{\n /**\n * 'usb' is android only, and now developing.\n */\n port: BRLMPrinterPort;\n\n /**\n * searchDuration is the time to end search for devices.\n * default is 15 seconds.\n * use only port is 'wifi' or 'bluetoothLowEnergy'.\n */\n searchDuration: number;\n /**\n * Android Bluetooth Classic only. Include only devices whose Bluetooth class\n * reports a printer. Defaults to false; ignored for other ports and on iOS.\n * This does not identify Brother devices. Devices with an unknown class are excluded when true.\n */\n bluetoothPrintersOnly?: boolean;\n}",
932
+ "text": "{\n /**\n * 'usb' is android only, and now developing.\n */\n port: BRLMPrinterPort;\n\n /**\n * searchDuration is the time to end search for devices.\n * default is 15 seconds.\n * Used for Wi-Fi, BLE, and the iOS Bluetooth accessory picker wait.\n * Timing out does not dismiss the system accessory picker; close it before retrying.\n */\n searchDuration: number;\n /**\n * Android Bluetooth Classic only. Include only devices whose Bluetooth class\n * reports a printer. Defaults to false; ignored for other ports and on iOS.\n * This does not identify Brother devices. Devices with an unknown class are excluded when true.\n */\n bluetoothPrintersOnly?: boolean;\n}",
933
933
  "complexTypes": [
934
934
  "BRLMPrinterPort"
935
935
  ]
@@ -121,7 +121,8 @@ export type BRLMSearchOption = {
121
121
  /**
122
122
  * searchDuration is the time to end search for devices.
123
123
  * default is 15 seconds.
124
- * use only port is 'wifi' or 'bluetoothLowEnergy'.
124
+ * Used for Wi-Fi, BLE, and the iOS Bluetooth accessory picker wait.
125
+ * Timing out does not dismiss the system accessory picker; close it before retrying.
125
126
  */
126
127
  searchDuration: number;
127
128
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/interfaces.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n BRLMPrinterLabelName,\n BRLMPrinterModelName,\n BRLMPrinterAutoCutType,\n BRLMPrinterCompressMode,\n BRLMPrinterHalftone,\n BRLMPrinterHalftoneThresholdType,\n BRLMPrinterHorizontalAlignment,\n BRLMPrinterNumberOfCopies,\n BRLMPrinterPrintQuality,\n BRLMPrinterImageRotation,\n BRLMPrinterScaleMode,\n BRLMPrinterScaleValueType,\n BRLMPrinterVerticalAlignment,\n BRLMPrinterCustomPaperType,\n BRLMPrinterCustomPaperUnit,\n BRLMPrinterPort,\n} from './brother-printer.enum';\n\nexport type BRLMChannelResult = {\n port: BRLMPrinterPort;\n modelName: string;\n serialNumber: string;\n macAddress: string;\n nodeName: string;\n location: string;\n\n /**\n * This need to connect to the printer.\n * wifi: IP Address\n * bluetooth: macAddress\n * bluetoothLowEnergy: modelName for bluetoothLowEnergy\n */\n channelInfo: string;\n};\n\nexport type BRLMPrintOptions = {\n encodedImage: string;\n\n /**\n * Should use enum BRLMPrinterModelName\n */\n modelName: BRLMPrinterModelName;\n} & Partial<BRLMChannelResult> &\n (BRLMPrinterQLModelSettings | BRLMPrinterTDModelSettings);\n\nexport type isChannelAvailableResult = {\n result: boolean;\n};\n\nexport type BRLMPrinterTDModelSettings = {\n /**\n * Should use enum BRKMPrinterCustomPaperType\n */\n paperType: BRLMPrinterCustomPaperType;\n\n /**\n * The width of the label. For example, the RD-U04J1 is 60.0 wide.\n */\n tapeWidth: number;\n\n /**\n * The length of the label. For example, the RD-U04J1 is 60.0 wide.\n */\n tapeLength: number;\n\n /**\n * It is the difference between a sticker and a mount.\n * For example, the RD-U04J1 is `1.0, 2.0, 1.0, 2.0`\n */\n marginTop: number;\n marginRight: number;\n marginBottom: number;\n marginLeft: number;\n\n /**\n * The spacing between seals. For example, the RD-U04J1 is 0.2.\n */\n gapLength: number;\n\n paperMarkPosition: number;\n paperMarkLength: number;\n\n /**\n * Should use enum BRKMPrinterCustomPaperUnit.\n * For example, the RD-U04J1 is mm.\n */\n paperUnit: BRLMPrinterCustomPaperUnit;\n};\n\nexport type BRLMPrinterQLModelSettings = {\n /**\n * Should use enum BRLMPrinterLabelName\n */\n labelName: BRLMPrinterLabelName;\n} & BRLMPrinterSettings;\n\n/**\n * These are optional. If these are not set, default values are assigned by the printer.\n */\nexport type BRLMPrinterSettings = {\n /**\n * The number of copies you print.\n */\n numberOfCopies?: BRLMPrinterNumberOfCopies;\n\n /**\n * Whether the auto-cut is enabled or not. If true, your printer cut the paper each page.\n */\n autoCut?: BRLMPrinterAutoCutType;\n\n /**\n * A scale mode that specifies how your data is scaled in a print area of your printer.\n */\n scaleMode?: BRLMPrinterScaleMode;\n\n /**\n * A scale value. This is effective when ScaleMode is ScaleValue.\n */\n scaleValue?: BRLMPrinterScaleValueType;\n\n /**\n * A way to rasterize your data.\n */\n halftone?: BRLMPrinterHalftone;\n\n /**\n * A threshold value. This is effective when the Halftone is Threshold.\n */\n halftoneThreshold?: BRLMPrinterHalftoneThresholdType;\n\n /**\n * An image rotation that specifies the angle in which your data is placed in the print area. Rotation direction is clockwise.\n */\n imageRotation?: BRLMPrinterImageRotation;\n\n /**\n * A vertical alignment that specifies how your data is placed in the printable area.\n */\n verticalAlignment?: BRLMPrinterVerticalAlignment;\n\n /**\n * A horizontal alignment that specifies how your data is placed in the printable area.\n */\n horizontalAlignment?: BRLMPrinterHorizontalAlignment;\n\n /**\n * A compress mode that specifies how to compress your data.\n * note: This is ios only.\n */\n compressMode?: BRLMPrinterCompressMode;\n\n /**\n * A priority that is print speed or print quality. Whether or not this has an effect is depend on your printer.\n */\n printQuality?: BRLMPrinterPrintQuality;\n};\n\nexport type BRLMSearchOption = {\n /**\n * 'usb' is android only, and now developing.\n */\n port: BRLMPrinterPort;\n\n /**\n * searchDuration is the time to end search for devices.\n * default is 15 seconds.\n * use only port is 'wifi' or 'bluetoothLowEnergy'.\n */\n searchDuration: number;\n /**\n * Android Bluetooth Classic only. Include only devices whose Bluetooth class\n * reports a printer. Defaults to false; ignored for other ports and on iOS.\n * This does not identify Brother devices. Devices with an unknown class are excluded when true.\n */\n bluetoothPrintersOnly?: boolean;\n};\n\nexport type ErrorInfo = {\n message: string;\n code: number;\n};\n"]}
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/interfaces.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n BRLMPrinterLabelName,\n BRLMPrinterModelName,\n BRLMPrinterAutoCutType,\n BRLMPrinterCompressMode,\n BRLMPrinterHalftone,\n BRLMPrinterHalftoneThresholdType,\n BRLMPrinterHorizontalAlignment,\n BRLMPrinterNumberOfCopies,\n BRLMPrinterPrintQuality,\n BRLMPrinterImageRotation,\n BRLMPrinterScaleMode,\n BRLMPrinterScaleValueType,\n BRLMPrinterVerticalAlignment,\n BRLMPrinterCustomPaperType,\n BRLMPrinterCustomPaperUnit,\n BRLMPrinterPort,\n} from './brother-printer.enum';\n\nexport type BRLMChannelResult = {\n port: BRLMPrinterPort;\n modelName: string;\n serialNumber: string;\n macAddress: string;\n nodeName: string;\n location: string;\n\n /**\n * This need to connect to the printer.\n * wifi: IP Address\n * bluetooth: macAddress\n * bluetoothLowEnergy: modelName for bluetoothLowEnergy\n */\n channelInfo: string;\n};\n\nexport type BRLMPrintOptions = {\n encodedImage: string;\n\n /**\n * Should use enum BRLMPrinterModelName\n */\n modelName: BRLMPrinterModelName;\n} & Partial<BRLMChannelResult> &\n (BRLMPrinterQLModelSettings | BRLMPrinterTDModelSettings);\n\nexport type isChannelAvailableResult = {\n result: boolean;\n};\n\nexport type BRLMPrinterTDModelSettings = {\n /**\n * Should use enum BRKMPrinterCustomPaperType\n */\n paperType: BRLMPrinterCustomPaperType;\n\n /**\n * The width of the label. For example, the RD-U04J1 is 60.0 wide.\n */\n tapeWidth: number;\n\n /**\n * The length of the label. For example, the RD-U04J1 is 60.0 wide.\n */\n tapeLength: number;\n\n /**\n * It is the difference between a sticker and a mount.\n * For example, the RD-U04J1 is `1.0, 2.0, 1.0, 2.0`\n */\n marginTop: number;\n marginRight: number;\n marginBottom: number;\n marginLeft: number;\n\n /**\n * The spacing between seals. For example, the RD-U04J1 is 0.2.\n */\n gapLength: number;\n\n paperMarkPosition: number;\n paperMarkLength: number;\n\n /**\n * Should use enum BRKMPrinterCustomPaperUnit.\n * For example, the RD-U04J1 is mm.\n */\n paperUnit: BRLMPrinterCustomPaperUnit;\n};\n\nexport type BRLMPrinterQLModelSettings = {\n /**\n * Should use enum BRLMPrinterLabelName\n */\n labelName: BRLMPrinterLabelName;\n} & BRLMPrinterSettings;\n\n/**\n * These are optional. If these are not set, default values are assigned by the printer.\n */\nexport type BRLMPrinterSettings = {\n /**\n * The number of copies you print.\n */\n numberOfCopies?: BRLMPrinterNumberOfCopies;\n\n /**\n * Whether the auto-cut is enabled or not. If true, your printer cut the paper each page.\n */\n autoCut?: BRLMPrinterAutoCutType;\n\n /**\n * A scale mode that specifies how your data is scaled in a print area of your printer.\n */\n scaleMode?: BRLMPrinterScaleMode;\n\n /**\n * A scale value. This is effective when ScaleMode is ScaleValue.\n */\n scaleValue?: BRLMPrinterScaleValueType;\n\n /**\n * A way to rasterize your data.\n */\n halftone?: BRLMPrinterHalftone;\n\n /**\n * A threshold value. This is effective when the Halftone is Threshold.\n */\n halftoneThreshold?: BRLMPrinterHalftoneThresholdType;\n\n /**\n * An image rotation that specifies the angle in which your data is placed in the print area. Rotation direction is clockwise.\n */\n imageRotation?: BRLMPrinterImageRotation;\n\n /**\n * A vertical alignment that specifies how your data is placed in the printable area.\n */\n verticalAlignment?: BRLMPrinterVerticalAlignment;\n\n /**\n * A horizontal alignment that specifies how your data is placed in the printable area.\n */\n horizontalAlignment?: BRLMPrinterHorizontalAlignment;\n\n /**\n * A compress mode that specifies how to compress your data.\n * note: This is ios only.\n */\n compressMode?: BRLMPrinterCompressMode;\n\n /**\n * A priority that is print speed or print quality. Whether or not this has an effect is depend on your printer.\n */\n printQuality?: BRLMPrinterPrintQuality;\n};\n\nexport type BRLMSearchOption = {\n /**\n * 'usb' is android only, and now developing.\n */\n port: BRLMPrinterPort;\n\n /**\n * searchDuration is the time to end search for devices.\n * default is 15 seconds.\n * Used for Wi-Fi, BLE, and the iOS Bluetooth accessory picker wait.\n * Timing out does not dismiss the system accessory picker; close it before retrying.\n */\n searchDuration: number;\n /**\n * Android Bluetooth Classic only. Include only devices whose Bluetooth class\n * reports a printer. Defaults to false; ignored for other ports and on iOS.\n * This does not identify Brother devices. Devices with an unknown class are excluded when true.\n */\n bluetoothPrintersOnly?: boolean;\n};\n\nexport type ErrorInfo = {\n message: string;\n code: number;\n};\n"]}
package/docs/search.md CHANGED
@@ -47,13 +47,15 @@ const stopSearching = async () => {
47
47
 
48
48
  Call `searchWifiPrinters` from the search button and await `stopSearching` when leaving the screen.
49
49
 
50
- On iOS, `bluetooth` first lists connected MFi printers. If none are connected, the app displays the system Bluetooth accessory picker so you can select and pair a printer. The search promise completes after the picker callback; picker errors reject the promise.
50
+ On iOS, `bluetooth` first lists connected MFi printers. If none are connected, the app displays the system Bluetooth accessory picker so you can select and pair a printer. The search promise completes after the picker callback; picker errors reject the promise. The picker wait rejects after `searchDuration` seconds (default 15, minimum 1), or when `cancelSearchBluetoothPrinter()` is called. Late picker results are ignored. Apple does not provide an API to dismiss this picker: close it before retrying. While the old picker remains open, another picker request rejects immediately. If the system never displays or closes it, restart the app before retrying.
51
+
52
+ When migrating to `SceneDelegate`, including during the Capacitor 8.5 update, also assign the scene window to `AppDelegate.window`, which the system accessory picker uses. After creating the scene window, set `(UIApplication.shared.delegate as? AppDelegate)?.window = window`. Without this reference, the picker may not appear even though the search has started.
51
53
 
52
54
  For BLE-capable printers, use `port: BRLMPrinterPort.bluetoothLowEnergy`. On iOS this uses `startBLESearch`, without the Bluetooth accessory picker. Pass the discovered printer's `channelInfo` (BLE local name) unchanged to `isChannelAvailable` or `printImage`. BLE search errors reject the search promise. QL-820NWB/QL-820NWBc do not support BLE printing; use `bluetooth` or `wifi` for these models.
53
55
 
54
56
  On Android, pair a Bluetooth printer in the system settings before calling `search` with `bluetooth`; the SDK lists paired printers and does not provide the iOS accessory picker. Bluetooth and BLE searches resolve after the search finishes, or reject on SDK errors. Android 12 and later request Nearby devices permissions; Android 11 and earlier request location permission for BLE. `isChannelAvailable` returns `false` when Bluetooth permission is missing.
55
57
 
56
- `searchDuration` applies to `wifi` and `bluetoothLowEnergy`. `usb` is Android only. If nothing is found, you get no error and no printers. Signatures are on the [API](/docs/api#brlmsearchoption) page.
58
+ `searchDuration` applies to `wifi`, `bluetoothLowEnergy`, and the iOS `bluetooth` accessory-picker wait. `usb` is Android only. Completed Wi-Fi/BLE searches with no results resolve without printers; iOS accessory-picker errors, cancellation, and wait timeouts reject the search. Cancellation also rejects an in-progress iOS Bluetooth initial scan and prevents its late result from opening the picker. Signatures are on the [API](/docs/api#brlmsearchoption) page.
57
59
 
58
60
  On Android, Bluetooth Classic searches return paired devices. To include only devices that report the Bluetooth Imaging/Printer class:
59
61
 
@@ -19,6 +19,8 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
19
19
  ]
20
20
  private var cancelRoutineWiFi: (() -> Void)?
21
21
  private var cancelRoutineBluetooth: (() -> Void)?
22
+ private var bluetoothAccessoryPickerActive = false
23
+ private var cancelAccessorySearch: (() -> Void)?
22
24
 
23
25
  @objc func printImage(_ call: CAPPluginCall) {
24
26
  let encodedImage: String = call.getString("encodedImage", "")
@@ -201,35 +203,85 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
201
203
  }
202
204
  }
203
205
 
204
- private func checkBLEChannel(_ call: CAPPluginCall) {
205
- DispatchQueue.global().async {
206
- let searcher = BRLMPrinterSearcher.startBluetoothSearch()
207
- if searcher.error.code != BRLMPrinterSearchErrorCode.noError {
208
- call.reject("Error - startBluetoothSearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: searcher.error.code))
209
- return
206
+ func checkBLEChannel(
207
+ _ call: CAPPluginCall,
208
+ scan: @escaping () -> (channels: [BRLMChannel], error: BRLMPrinterSearchErrorCode) = {
209
+ let result = BRLMPrinterSearcher.startBluetoothSearch()
210
+ return (result.channels, result.error.code)
211
+ },
212
+ startAccessorySearch: @escaping (@escaping (BRLMPrinterSearchResult) -> Void) -> Void = BRLMPrinterSearcher.startBluetoothAccessorySearch
213
+ ) {
214
+ DispatchQueue.main.async {
215
+ var cancelled = false
216
+ self.cancelAccessorySearch = {
217
+ cancelled = true
218
+ self.cancelAccessorySearch = nil
219
+ call.reject("Bluetooth accessory search cancelled.")
210
220
  }
211
- if searcher.channels.isEmpty {
221
+ DispatchQueue.global().async {
222
+ let result = scan()
212
223
  DispatchQueue.main.async {
213
- BRLMPrinterSearcher.startBluetoothAccessorySearch { result in
214
- guard result.error.code == BRLMPrinterSearchErrorCode.noError else {
215
- call.reject("Error - startBluetoothAccessorySearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: result.error.code))
216
- return
217
- }
218
- for channel in result.channels {
219
- self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
220
- }
221
- call.resolve()
224
+ guard !cancelled else { return }
225
+ self.cancelAccessorySearch = nil
226
+ guard result.error == .noError else {
227
+ call.reject("Error - startBluetoothSearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: result.error))
228
+ return
222
229
  }
230
+ if result.channels.isEmpty {
231
+ self.searchBluetoothAccessory(call, startSearch: startAccessorySearch)
232
+ return
233
+ }
234
+ for channel in result.channels {
235
+ self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
236
+ }
237
+ call.resolve()
223
238
  }
224
- return
225
239
  }
226
- for channel in searcher.channels {
227
- NSLog(channel.channelInfo)
228
- self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
229
- }
230
- call.resolve()
231
240
  }
241
+ }
232
242
 
243
+ func searchBluetoothAccessory(
244
+ _ call: CAPPluginCall,
245
+ startSearch: (@escaping (BRLMPrinterSearchResult) -> Void) -> Void = BRLMPrinterSearcher.startBluetoothAccessorySearch
246
+ ) {
247
+ guard !self.bluetoothAccessoryPickerActive else {
248
+ call.reject("Bluetooth accessory picker is still open. Close it before searching again.")
249
+ return
250
+ }
251
+ self.bluetoothAccessoryPickerActive = true
252
+ // Only the OS callback closes the picker; timeout/cancellation ends our wait, not the system UI.
253
+ var finished = false
254
+ let finish = { () -> Bool in
255
+ guard !finished else { return false }
256
+ finished = true
257
+ self.cancelAccessorySearch = nil
258
+ return true
259
+ }
260
+ let timeout = DispatchWorkItem {
261
+ guard finish() else { return }
262
+ call.reject("Bluetooth accessory search timed out. Close the accessory picker before retrying.")
263
+ }
264
+ self.cancelAccessorySearch = {
265
+ guard finish() else { return }
266
+ timeout.cancel()
267
+ call.reject("Bluetooth accessory search cancelled.")
268
+ }
269
+ DispatchQueue.main.asyncAfter(deadline: .now() + Double(max(1, call.getInt("searchDuration", 15))), execute: timeout)
270
+ startSearch { result in
271
+ DispatchQueue.main.async {
272
+ self.bluetoothAccessoryPickerActive = false
273
+ guard finish() else { return }
274
+ timeout.cancel()
275
+ guard result.error.code == BRLMPrinterSearchErrorCode.noError else {
276
+ call.reject("Error - startBluetoothAccessorySearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: result.error.code))
277
+ return
278
+ }
279
+ for channel in result.channels {
280
+ self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
281
+ }
282
+ call.resolve()
283
+ }
284
+ }
233
285
  }
234
286
 
235
287
  private func searchBLEPrinter(_ call: CAPPluginCall) {
@@ -280,10 +332,13 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
280
332
  }
281
333
 
282
334
  @objc func cancelSearchBluetoothPrinter(_ call: CAPPluginCall) {
283
- DispatchQueue.global().async {
284
- self.cancelRoutineBluetooth?()
285
- self.cancelRoutineBluetooth = nil
286
- call.resolve()
335
+ DispatchQueue.main.async {
336
+ self.cancelAccessorySearch?()
337
+ DispatchQueue.global().async {
338
+ self.cancelRoutineBluetooth?()
339
+ self.cancelRoutineBluetooth = nil
340
+ call.resolve()
341
+ }
287
342
  }
288
343
  }
289
344
  }
@@ -2,6 +2,89 @@ import XCTest
2
2
  import Capacitor
3
3
  @testable import BrotherPrintPlugin
4
4
 
5
+ class BluetoothAccessorySearchTests: XCTestCase {
6
+ func testCancellationDuringInitialScanDoesNotOpenPicker() {
7
+ let scanStarted = expectation(description: "Initial scan started")
8
+ let scanFinished = expectation(description: "Initial scan returned")
9
+ let pickerOpened = expectation(description: "Cancelled scan must not open picker")
10
+ pickerOpened.isInverted = true
11
+ let releaseScan = DispatchSemaphore(value: 0)
12
+ let plugin = BrotherPrintPlugin()
13
+ var rejections = 0
14
+ let call = CAPPluginCall(callbackId: "initial-scan", methodName: "search", options: [:], success: { _, _ in
15
+ XCTFail("Cancelled search must not resolve")
16
+ }, error: { error in
17
+ rejections += 1
18
+ XCTAssertTrue(error?.message.contains("cancelled") == true)
19
+ })!
20
+ plugin.checkBLEChannel(call, scan: {
21
+ scanStarted.fulfill()
22
+ XCTAssertEqual(releaseScan.wait(timeout: .now() + 3), .success)
23
+ scanFinished.fulfill()
24
+ return ([], .noError)
25
+ }, startAccessorySearch: { _ in
26
+ pickerOpened.fulfill()
27
+ })
28
+ wait(for: [scanStarted], timeout: 3)
29
+ let cancel = CAPPluginCall(callbackId: "stop", methodName: "cancelSearchBluetoothPrinter", options: [:], success: { _, _ in
30
+ releaseScan.signal()
31
+ }, error: { _ in
32
+ XCTFail("Cancellation must resolve")
33
+ })!
34
+ plugin.cancelSearchBluetoothPrinter(cancel)
35
+ wait(for: [scanFinished], timeout: 3)
36
+ wait(for: [pickerOpened], timeout: 0.2)
37
+ XCTAssertEqual(rejections, 1)
38
+ }
39
+
40
+ func testMissingPickerCallbackTimesOutAndDoesNotOpenAnotherPicker() {
41
+ let timedOut = expectation(description: "Search rejects without a picker callback")
42
+ let plugin = BrotherPrintPlugin()
43
+ var starts = 0
44
+ let call = CAPPluginCall(callbackId: "timeout", methodName: "search", options: ["searchDuration": 1], success: { _, _ in
45
+ XCTFail("Search must reject")
46
+ }, error: { error in
47
+ XCTAssertTrue(error?.message.contains("timed out") == true)
48
+ let retry = CAPPluginCall(callbackId: "retry", methodName: "search", options: [:], success: { _, _ in
49
+ XCTFail("A second picker must not open")
50
+ }, error: { error in
51
+ XCTAssertTrue(error?.message.contains("still open") == true)
52
+ })!
53
+ plugin.searchBluetoothAccessory(retry) { _ in starts += 1 }
54
+ XCTAssertEqual(starts, 1)
55
+ timedOut.fulfill()
56
+ })!
57
+ DispatchQueue.main.async {
58
+ plugin.searchBluetoothAccessory(call) { _ in starts += 1 }
59
+ }
60
+ wait(for: [timedOut], timeout: 3)
61
+ }
62
+
63
+ func testCancellationRejectsOnlyOnceEvenAfterDeadline() {
64
+ let finished = expectation(description: "Cancellation stays settled after the deadline")
65
+ let plugin = BrotherPrintPlugin()
66
+ var rejections = 0
67
+ let call = CAPPluginCall(callbackId: "cancel", methodName: "search", options: ["searchDuration": 1], success: { _, _ in
68
+ XCTFail("Search must reject")
69
+ }, error: { error in
70
+ rejections += 1
71
+ XCTAssertTrue(error?.message.contains("cancelled") == true)
72
+ })!
73
+ let cancel = CAPPluginCall(callbackId: "stop", methodName: "cancelSearchBluetoothPrinter", options: [:], success: { _, _ in }, error: { _ in
74
+ XCTFail("Cancellation must resolve")
75
+ })!
76
+ DispatchQueue.main.async {
77
+ plugin.searchBluetoothAccessory(call) { _ in }
78
+ plugin.cancelSearchBluetoothPrinter(cancel)
79
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) {
80
+ XCTAssertEqual(rejections, 1)
81
+ finished.fulfill()
82
+ }
83
+ }
84
+ wait(for: [finished], timeout: 3)
85
+ }
86
+ }
87
+
5
88
  class PrintImageValidationTests: XCTestCase {
6
89
  func testInvalidImagesRejectBeforeOpeningPrinter() {
7
90
  for image in ["", "A", "SGVsbG8=", "iVBORw0KGgo="] {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rdlabo/capacitor-brotherprint",
3
3
  "private": false,
4
- "version": "8.2.2",
4
+ "version": "8.2.3",
5
5
  "description": "Capacitor plugin for Brother Print SDK",
6
6
  "main": "dist/plugin.cjs.js",
7
7
  "module": "dist/esm/index.js",