Compare commits
4 Commits
master
...
fix/resour
| Author | SHA1 | Date |
|---|---|---|
|
|
e6dd6889bb | |
|
|
891d499ee4 | |
|
|
cc03104a72 | |
|
|
4e6c022a23 |
|
|
@ -1,7 +1,7 @@
|
||||||
PODS:
|
PODS:
|
||||||
- AppSwizzle (1.3.1)
|
- AppSwizzle (1.3.1)
|
||||||
- ReCaptcha/Core (1.4.1)
|
- ReCaptcha/Core (1.4.2)
|
||||||
- ReCaptcha/RxSwift (1.4.1):
|
- ReCaptcha/RxSwift (1.4.2):
|
||||||
- ReCaptcha/Core
|
- ReCaptcha/Core
|
||||||
- RxSwift (~> 4.3)
|
- RxSwift (~> 4.3)
|
||||||
- RxAtomic (4.4.0)
|
- RxAtomic (4.4.0)
|
||||||
|
|
@ -36,7 +36,7 @@ EXTERNAL SOURCES:
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
AppSwizzle: db36e436f56110d93e5ae0147683435df593cabc
|
AppSwizzle: db36e436f56110d93e5ae0147683435df593cabc
|
||||||
ReCaptcha: 520a707a38dfbb1e5de812aa3c041df60bd31827
|
ReCaptcha: 9a0e1c02a9db9dface31cca63515e28fc3ed6ba8
|
||||||
RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f
|
RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f
|
||||||
RxBlocking: 138ad53217434444d6eeeb4fb406a45431d92e31
|
RxBlocking: 138ad53217434444d6eeeb4fb406a45431d92e31
|
||||||
RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749
|
RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0910"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "NO"
|
||||||
|
buildForArchiving = "NO"
|
||||||
|
buildForAnalyzing = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
||||||
|
BuildableName = "ReCaptcha_UITests.xctest"
|
||||||
|
BlueprintName = "ReCaptcha_UITests"
|
||||||
|
ReferencedContainer = "container:ReCaptcha.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
||||||
|
BuildableName = "ReCaptcha_UITests.xctest"
|
||||||
|
BlueprintName = "ReCaptcha_UITests"
|
||||||
|
ReferencedContainer = "container:ReCaptcha.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
||||||
|
BuildableName = "ReCaptcha_UITests.xctest"
|
||||||
|
BlueprintName = "ReCaptcha_UITests"
|
||||||
|
ReferencedContainer = "container:ReCaptcha.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "F28FAC9B200E425600E14987"
|
||||||
|
BuildableName = "ReCaptcha_UITests.xctest"
|
||||||
|
BlueprintName = "ReCaptcha_UITests"
|
||||||
|
ReferencedContainer = "container:ReCaptcha.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
|
|
@ -164,4 +164,23 @@ class ReCaptchaDecoder__Tests: XCTestCase {
|
||||||
// Check
|
// Check
|
||||||
XCTAssertEqual(result, .didLoad)
|
XCTAssertEqual(result, .didLoad)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test__Decode__Error_Setup_Failed() {
|
||||||
|
let exp = expectation(description: "send error")
|
||||||
|
var result: Result?
|
||||||
|
|
||||||
|
assertResult = { res in
|
||||||
|
result = res
|
||||||
|
exp.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send
|
||||||
|
let message = MockMessage(message: ["error": 27])
|
||||||
|
decoder.send(message: message)
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 1)
|
||||||
|
|
||||||
|
// Check
|
||||||
|
XCTAssertEqual(result, .error(.failedSetup))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ extension ReCaptchaError: Equatable {
|
||||||
case (.htmlLoadError, .htmlLoadError),
|
case (.htmlLoadError, .htmlLoadError),
|
||||||
(.apiKeyNotFound, .apiKeyNotFound),
|
(.apiKeyNotFound, .apiKeyNotFound),
|
||||||
(.baseURLNotFound, .baseURLNotFound),
|
(.baseURLNotFound, .baseURLNotFound),
|
||||||
(.wrongMessageFormat, .wrongMessageFormat):
|
(.wrongMessageFormat, .wrongMessageFormat),
|
||||||
|
(.failedSetup, .failedSetup):
|
||||||
return true
|
return true
|
||||||
case (.unexpected(let lhe as NSError), .unexpected(let rhe as NSError)):
|
case (.unexpected(let lhe as NSError), .unexpected(let rhe as NSError)):
|
||||||
return lhe == rhe
|
return lhe == rhe
|
||||||
|
|
@ -25,11 +26,12 @@ extension ReCaptchaError: Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func random() -> ReCaptchaError {
|
static func random() -> ReCaptchaError {
|
||||||
switch arc4random_uniform(4) {
|
switch arc4random_uniform(5) {
|
||||||
case 0: return .htmlLoadError
|
case 0: return .htmlLoadError
|
||||||
case 1: return .apiKeyNotFound
|
case 1: return .apiKeyNotFound
|
||||||
case 2: return .baseURLNotFound
|
case 2: return .baseURLNotFound
|
||||||
case 3: return .wrongMessageFormat
|
case 3: return .wrongMessageFormat
|
||||||
|
case 4: return .failedSetup
|
||||||
default: return .unexpected(NSError())
|
default: return .unexpected(NSError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@
|
||||||
var endpoint = "${endpoint}";
|
var endpoint = "${endpoint}";
|
||||||
var shouldFail = ${shouldFail};
|
var shouldFail = ${shouldFail};
|
||||||
|
|
||||||
var post = function(value) {
|
var post = (value) => {
|
||||||
window.webkit.messageHandlers.recaptcha.postMessage(value);
|
window.webkit.messageHandlers.recaptcha.postMessage(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
var execute = function() {
|
var execute = () => {
|
||||||
if (shouldFail) {
|
if (shouldFail) {
|
||||||
post("error");
|
post("error");
|
||||||
}
|
}
|
||||||
|
|
@ -19,10 +19,12 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var reset = function() {
|
var reset = () => {
|
||||||
shouldFail = false;
|
shouldFail = false;
|
||||||
post({action: "didLoad"});
|
post({ action: "didLoad" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
post({ action: "didLoad" });
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -2,61 +2,81 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var post = function(value) {
|
const post = (value) => window.webkit.messageHandlers.recaptcha.postMessage(value);
|
||||||
window.webkit.messageHandlers.recaptcha.postMessage(value);
|
console.log = (message) => post({ log: message });
|
||||||
|
|
||||||
|
let observers = []
|
||||||
|
const observeDOM = (element, completion) => {
|
||||||
|
const obs = new MutationObserver(completion);
|
||||||
|
obs.observe(element, {
|
||||||
|
attributes: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributeFilter: ['style'],
|
||||||
|
});
|
||||||
|
|
||||||
|
observers.push(obs);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log = function(message) {
|
const clearObservers = () => {
|
||||||
post({log: message});
|
observers.forEach(o => o.disconnect());
|
||||||
|
observers = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
var showReCaptcha = function() {
|
const execute = () => {
|
||||||
console.log("showReCaptcha");
|
console.log('executing');
|
||||||
post({action: "showReCaptcha"});
|
|
||||||
};
|
|
||||||
|
|
||||||
var observeDOM = function(element, completion) {
|
|
||||||
new MutationObserver(function(mutations) {
|
|
||||||
mutations.forEach(function(mutationRecord) {
|
|
||||||
completion();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.observe(element, {attributes: true, attributeFilter: ['style']})
|
|
||||||
};
|
|
||||||
|
|
||||||
var execute = function() {
|
|
||||||
console.log("executing");
|
|
||||||
|
|
||||||
// Removes ReCaptcha dismissal when clicking outside div area
|
// Removes ReCaptcha dismissal when clicking outside div area
|
||||||
try {
|
try {
|
||||||
document.getElementsByTagName("div")[4].outerHTML = ""
|
document.getElementsByTagName('div')[4].outerHTML = ''
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listens to changes on the div element that presents the ReCaptcha challenge
|
try {
|
||||||
observeDOM(document.getElementsByTagName("div")[3], showReCaptcha);
|
// Listens to changes on the div element that presents the ReCaptcha challenge
|
||||||
|
observeDOM(document.getElementsByTagName('div')[3], () => {
|
||||||
|
post({ action: 'showReCaptcha' });
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
post({ error: 27 })
|
||||||
|
}
|
||||||
|
|
||||||
grecaptcha.execute();
|
grecaptcha.execute();
|
||||||
};
|
};
|
||||||
|
|
||||||
var onSubmit = function(token) {
|
const reset = () => {
|
||||||
console.log(token);
|
console.log('resetting');
|
||||||
post({token: token});
|
grecaptcha.reset();
|
||||||
};
|
grecaptcha.ready(() => post({ action: 'didLoad' }));
|
||||||
|
};
|
||||||
|
|
||||||
var onloadCallback = function() {
|
var onloadCallback = () => {
|
||||||
console.log("did load");
|
|
||||||
grecaptcha.render('submit', {
|
grecaptcha.render('submit', {
|
||||||
'sitekey' : '${apiKey}',
|
sitekey: '${apiKey}',
|
||||||
'callback' : onSubmit,
|
callback: (token) => {
|
||||||
'size': 'invisible'
|
console.log(token);
|
||||||
|
post({ token });
|
||||||
|
clearObservers();
|
||||||
|
},
|
||||||
|
size: 'invisible'
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
var reset = function() {
|
grecaptcha.ready(() => {
|
||||||
console.log("resetting");
|
observeDOM(document.getElementById('body'), mut => {
|
||||||
grecaptcha.reset();
|
const success = !!mut.find(
|
||||||
|
({ addedNodes }) =>
|
||||||
|
Array.from(addedNodes.values())
|
||||||
|
.find(({ nodeName, name }) =>
|
||||||
|
nodeName === 'IFRAME' && !!name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
post({ action: 'didLoad' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,12 @@ fileprivate extension ReCaptchaDecoder.Result {
|
||||||
if let token = response["token"] as? String {
|
if let token = response["token"] as? String {
|
||||||
return .token(token)
|
return .token(token)
|
||||||
}
|
}
|
||||||
|
else if let message = response["log"] as? String {
|
||||||
|
return .log(message)
|
||||||
|
}
|
||||||
|
else if let message = response["error"] as? Int {
|
||||||
|
return .error(.failedSetup)
|
||||||
|
}
|
||||||
|
|
||||||
if let action = response["action"] as? String {
|
if let action = response["action"] as? String {
|
||||||
switch action {
|
switch action {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ public enum ReCaptchaError: Error, CustomStringConvertible {
|
||||||
/// Received an unexpected message from javascript
|
/// Received an unexpected message from javascript
|
||||||
case wrongMessageFormat
|
case wrongMessageFormat
|
||||||
|
|
||||||
|
/// ReCaptcha setup failed
|
||||||
|
case failedSetup
|
||||||
|
|
||||||
/// A human-readable description for each error
|
/// A human-readable description for each error
|
||||||
public var description: String {
|
public var description: String {
|
||||||
|
|
@ -43,6 +45,14 @@ public enum ReCaptchaError: Error, CustomStringConvertible {
|
||||||
|
|
||||||
case .wrongMessageFormat:
|
case .wrongMessageFormat:
|
||||||
return "Unexpected message from javascript"
|
return "Unexpected message from javascript"
|
||||||
|
|
||||||
|
case .failedSetup:
|
||||||
|
// swiftlint:disable line_length
|
||||||
|
return """
|
||||||
|
⚠️ WARNING! ReCaptcha wasn't successfully configured. Please double check your ReCaptchaKey and ReCaptchaDomain.
|
||||||
|
Also check that you're using ReCaptcha's **SITE KEY** for client side integration.
|
||||||
|
"""
|
||||||
|
// swiftlint:enable line_length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ import WebKit
|
||||||
/** Handles comunications with the webview containing the ReCaptcha challenge.
|
/** Handles comunications with the webview containing the ReCaptcha challenge.
|
||||||
*/
|
*/
|
||||||
internal class ReCaptchaWebViewManager {
|
internal class ReCaptchaWebViewManager {
|
||||||
|
enum JSCommand: String {
|
||||||
|
case execute = "execute();"
|
||||||
|
case reset = "reset();"
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate struct Constants {
|
fileprivate struct Constants {
|
||||||
static let ExecuteJSCommand = "execute();"
|
static let ExecuteJSCommand = "execute();"
|
||||||
|
|
@ -53,24 +57,11 @@ internal class ReCaptchaWebViewManager {
|
||||||
fileprivate var decoder: ReCaptchaDecoder!
|
fileprivate var decoder: ReCaptchaDecoder!
|
||||||
|
|
||||||
/// Indicates if the script has already been loaded by the `webView`
|
/// Indicates if the script has already been loaded by the `webView`
|
||||||
fileprivate var didFinishLoading = false { // webView.isLoading does not work for WKWebview.loadHTMLString
|
fileprivate var didFinishLoading = false
|
||||||
didSet {
|
|
||||||
if didFinishLoading && completion != nil {
|
|
||||||
// User has requested for validation
|
|
||||||
// A small delay is necessary to allow JS to wrap its operations and avoid errors.
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
|
|
||||||
self?.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The observer for `.UIWindowDidBecomeVisible`
|
/// The observer for `.UIWindowDidBecomeVisible`
|
||||||
fileprivate var observer: NSObjectProtocol?
|
fileprivate var observer: NSObjectProtocol?
|
||||||
|
|
||||||
/// The observer for `\WKWebView.estimatedProgress`
|
|
||||||
fileprivate var loadingObservation: NSKeyValueObservation?
|
|
||||||
|
|
||||||
/// The endpoint url being used
|
/// The endpoint url being used
|
||||||
fileprivate var endpoint: String
|
fileprivate var endpoint: String
|
||||||
|
|
||||||
|
|
@ -83,9 +74,6 @@ internal class ReCaptchaWebViewManager {
|
||||||
webview.accessibilityIdentifier = "webview"
|
webview.accessibilityIdentifier = "webview"
|
||||||
webview.accessibilityTraits = UIAccessibilityTraits.link
|
webview.accessibilityTraits = UIAccessibilityTraits.link
|
||||||
webview.isHidden = true
|
webview.isHidden = true
|
||||||
self.loadingObservation = webview.observe(\.estimatedProgress, options: [.new]) { [weak self] _, change in
|
|
||||||
self?.didFinishLoading = change.newValue == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return webview
|
return webview
|
||||||
}()
|
}()
|
||||||
|
|
@ -135,7 +123,7 @@ internal class ReCaptchaWebViewManager {
|
||||||
webView.isHidden = false
|
webView.isHidden = false
|
||||||
view.addSubview(webView)
|
view.addSubview(webView)
|
||||||
|
|
||||||
execute()
|
executeJS(command: .execute)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -150,14 +138,9 @@ internal class ReCaptchaWebViewManager {
|
||||||
The reset is achieved by calling `grecaptcha.reset()` on the JS API.
|
The reset is achieved by calling `grecaptcha.reset()` on the JS API.
|
||||||
*/
|
*/
|
||||||
func reset() {
|
func reset() {
|
||||||
didFinishLoading = false
|
|
||||||
configureWebViewDispatchToken = UUID()
|
configureWebViewDispatchToken = UUID()
|
||||||
|
executeJS(command: .reset)
|
||||||
webView.evaluateJavaScript(Constants.ResetCommand) { [weak self] _, error in
|
didFinishLoading = false
|
||||||
if let error = error {
|
|
||||||
self?.decoder.send(error: .unexpected(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,22 +149,6 @@ internal class ReCaptchaWebViewManager {
|
||||||
/** Private methods for ReCaptchaWebViewManager
|
/** Private methods for ReCaptchaWebViewManager
|
||||||
*/
|
*/
|
||||||
fileprivate extension ReCaptchaWebViewManager {
|
fileprivate extension ReCaptchaWebViewManager {
|
||||||
/** Executes the JS command that loads the ReCaptcha challenge.
|
|
||||||
This method has no effect if the webview hasn't finished loading.
|
|
||||||
*/
|
|
||||||
func execute() {
|
|
||||||
guard didFinishLoading else {
|
|
||||||
// Hasn't finished loading the HTML yet
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
webView.evaluateJavaScript(Constants.ExecuteJSCommand) { [weak self] _, error in
|
|
||||||
if let error = error {
|
|
||||||
self?.decoder.send(error: .unexpected(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
- returns: An instance of `WKWebViewConfiguration`
|
- returns: An instance of `WKWebViewConfiguration`
|
||||||
|
|
||||||
|
|
@ -223,12 +190,14 @@ fileprivate extension ReCaptchaWebViewManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
case .didLoad:
|
case .didLoad:
|
||||||
// For testing purposes
|
|
||||||
didFinishLoading = true
|
didFinishLoading = true
|
||||||
|
if completion != nil {
|
||||||
|
executeJS(command: .execute)
|
||||||
|
}
|
||||||
|
|
||||||
case .log(let message):
|
case .log(let message):
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
print("[JS LOG]:", message)
|
print("[JS LOG]:", message)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -249,4 +218,24 @@ fileprivate extension ReCaptchaWebViewManager {
|
||||||
NotificationCenter.default.removeObserver(observer)
|
NotificationCenter.default.removeObserver(observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- parameters:
|
||||||
|
- command: The JavaScript command to be executed
|
||||||
|
|
||||||
|
Executes the JS command that loads the ReCaptcha challenge. This method has no effect if the webview hasn't
|
||||||
|
finished loading.
|
||||||
|
*/
|
||||||
|
func executeJS(command: JSCommand) {
|
||||||
|
guard didFinishLoading else {
|
||||||
|
// Hasn't finished loading all the resources
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
webView.evaluateJavaScript(command.rawValue) { [weak self] _, error in
|
||||||
|
if let error = error {
|
||||||
|
self?.decoder.send(error: .unexpected(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ default_platform :ios
|
||||||
platform :ios do
|
platform :ios do
|
||||||
skip_docs
|
skip_docs
|
||||||
|
|
||||||
devices = ["iPhone XR (~> 12)"]
|
devices = ["iPhone X (~> 12)"]
|
||||||
devices << "iPhone X (~> 11)" if !Helper.is_ci?
|
# devices << "iPhone X (~> 11)" if !Helper.is_ci?
|
||||||
devices << "iPhone 7 (~> 10)" if !Helper.is_ci?
|
# devices << "iPhone 7 (~> 10)" if !Helper.is_ci?
|
||||||
devices << "iPhone 6s (~> 9)" if !Helper.is_ci?
|
# devices << "iPhone 6s (~> 9)" if !Helper.is_ci?
|
||||||
|
|
||||||
desc "Runs the following lanes:\n- test\n- pod_lint\n- carthage_lint"
|
desc "Runs the following lanes:\n- test\n- pod_lint\n- carthage_lint"
|
||||||
lane :ci do
|
lane :ci do
|
||||||
|
|
@ -57,12 +57,21 @@ platform :ios do
|
||||||
code_coverage: true,
|
code_coverage: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_ci
|
if Helper.is_ci?
|
||||||
codecov(
|
codecov(
|
||||||
project_name: 'ReCaptcha',
|
project_name: 'ReCaptcha',
|
||||||
use_xcodeplist: true,
|
use_xcodeplist: true,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
puts "Running UI Tests"
|
||||||
|
scan(
|
||||||
|
test_without_building: true,
|
||||||
|
devices: self.select_similar_simulator(devices),
|
||||||
|
scheme: "ReCaptcha_UITests",
|
||||||
|
workspace: "Example/ReCaptcha.xcworkspace",
|
||||||
|
code_coverage: true,
|
||||||
|
)
|
||||||
|
|
||||||
puts "Not CI: Skipping coverage files upload"
|
puts "Not CI: Skipping coverage files upload"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue