diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index c1d27121f9ee..0aaf4bcf57aa 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.0 + +* Adds implementation of the `loadFlutterAsset` method from the platform interface. + ## 2.6.0 * Implements new cookie manager for setting cookies and providing initial cookies. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html new file mode 100644 index 000000000000..9895dd3ce6cb --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html @@ -0,0 +1,20 @@ + + + +
++ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +
+ + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css new file mode 100644 index 000000000000..c2140b8b0fd8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m index 6c7c6bffbc3f..976b9c46579a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m @@ -164,7 +164,120 @@ - (void)testLoadFileFailsWithInvalidPath { OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); } -- (void)testLoadFileSucceedsWithBaseUrl { +- (void)testLoadFlutterAssetSucceeds { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + NSURL *url = [NSURL URLWithString:[@"file:///" stringByAppendingString:filePath]]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(url)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return successful result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + XCTAssertNil(result); + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMVerify([mockWebView loadFileURL:url + allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); +} + +- (void)testLoadFlutterAssetFailsWithInvalidKey { + NSArray *resultExpectations = @[ + [self expectationWithDescription:@"Should return failed result when argument is nil."], + [self expectationWithDescription: + @"Should return failed result when argument is not of type NSString*."], + [self expectationWithDescription: + @"Should return failed result when argument is an empty string."], + ]; + + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:nil] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is nil."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[0] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@(10)] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is not of type NSString."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[1] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@""] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument contains an empty string."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[2] fulfill]; + }]; + + [self waitForExpectations:resultExpectations timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadFlutterAssetFailsWithParsingError { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(nil)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return failed result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller + onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + FlutterError *expected = [FlutterError + errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Failed parsing file path for supplied key." + details:[NSString + stringWithFormat: + @"Failed to convert path '%@' into NSURL for key '%@'.", + filePath, @"assets/file.html"]]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadHtmlStringSucceedsWithBaseUrl { NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; @@ -189,7 +302,7 @@ - (void)testLoadFileSucceedsWithBaseUrl { OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]); } -- (void)testLoadFileSucceedsWithoutBaseUrl { +- (void)testLoadHtmlStringSucceedsWithoutBaseUrl { XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; FLTWebViewController *controller = diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index ce7548a87cab..d4e0ec4aba0c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -171,6 +171,7 @@ enum _MenuOptions { listCache, clearCache, navigationDelegate, + loadFlutterAsset, loadLocalFile, loadHtmlString, doPostRequest, @@ -214,6 +215,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case _MenuOptions.loadFlutterAsset: + _onLoadFlutterAssetExample(controller.data!, context); + break; case _MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -261,6 +265,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadFlutterAsset, + child: Text('Load Flutter Asset'), + ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -355,6 +363,11 @@ class _SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future