TF-3221 Handle mailto with additional cc and bcc

This commit is contained in:
DatDang
2024-10-25 11:28:59 +07:00
committed by Dat H. Pham
parent 51a4a1b9b7
commit 45581e4177
9 changed files with 292 additions and 59 deletions
@@ -18,13 +18,13 @@ Summary of URI schemes that can interact with Twake Mail:
- `/mailto?uri=user@example.com` - `/mailto?uri=user@example.com`
- `/mailto/?uri=mailto:user@example.com&subject=TwakeMail&body=HelloWorld` - `/mailto/?uri=mailto:user@example.com&subject=TwakeMail&body=HelloWorld`
- `/mailto/?uri=mailto:user1@example.com,user2@example.com,user3@example.com&subject=TwakeMail&body=HelloWorld` - `/mailto/?uri=mailto:user1@example.com,user2@example.com,user3@example.com&to=user1@example.com,user2@example.com,user3@example.com&cc=user1@example.com,user2@example.com,user3@example.com&bcc=user1@example.com,user2@example.com,user3@example.com&subject=TwakeMail&body=HelloWorld`
2. URI scheme encoded 2. URI scheme encoded
- `%2Fmailto%3Furi%3Duser%40example.com` - `%2Fmailto%3Furi%3Duser%40example.com`
- `%2Fmailto%2F%3Furi%3Dmailto%3Auser%40example.com%26subject%3DTwakeMail%26body%3DHelloWorld` - `%2Fmailto%2F%3Furi%3Dmailto%3Auser%40example.com%26subject%3DTwakeMail%26body%3DHelloWorld`
- `%2Fmailto%2F%3Furi%3Dmailto%3Auser1%40example.com%2Cuser2%40example.com%2Cuser3%40example.com%26subject%3DTwakeMail%26body%3DHelloWorld` - `%2Fmailto%2F%3Furi%3Dmailto%3Auser1%40example.com%2Cuser2%40example.com%2Cuser3%40example.com%26to=user1%40example.com%2Cuser2%40example.com%2Cuser3%40example.com%26cc=user1%40example.com%2Cuser2%40example.com%2Cuser3%40example.com%26bcc=user1%40example.com%2Cuser2%40example.com%2Cuser3%40example.com%26subject%3DTwakeMail%26body%3DHelloWorld`
## Consequences ## Consequences
@@ -629,6 +629,16 @@ class ComposerController extends BaseController
isInitialRecipient.value = true; isInitialRecipient.value = true;
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
if (arguments.cc?.isNotEmpty == true) {
listCcEmailAddress = arguments.cc!;
ccRecipientState.value = PrefixRecipientState.enabled;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
}
if (arguments.bcc?.isNotEmpty == true) {
bccRecipientState.value = PrefixRecipientState.enabled;
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
listBccEmailAddress = arguments.bcc!;
}
_getEmailContentFromMailtoUri(arguments.body ?? ''); _getEmailContentFromMailtoUri(arguments.body ?? '');
_updateStatusEmailSendButton(); _updateStatusEmailSendButton();
break; break;
@@ -1729,10 +1739,15 @@ class ComposerController extends BaseController
if (bccRecipientState.value == PrefixRecipientState.disabled) { if (bccRecipientState.value == PrefixRecipientState.disabled) {
bccRecipientState.value = PrefixRecipientState.enabled; bccRecipientState.value = PrefixRecipientState.enabled;
} }
listBccEmailAddress = listEmailAddress.toList(); if (composerArguments.value?.emailActionType == EmailActionType.composeFromMailtoUri) {
listBccEmailAddress = {...listEmailAddress, ...?composerArguments.value?.bcc}.toList();
} else {
listBccEmailAddress = listEmailAddress.toList();
}
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE; ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.refresh();
_updateStatusEmailSendButton(); _updateStatusEmailSendButton();
} }
@@ -1159,13 +1159,21 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
} }
Future<void> openMailToLink(Uri? uri) async { Future<void> openMailToLink(Uri? uri) async {
if (uri == null) return;
final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink(uri.toString());
log('SingleEmailController::openMailToLink(): ${uri.toString()}'); log('SingleEmailController::openMailToLink(): ${uri.toString()}');
String address = uri?.path ?? ''; if (!RouteUtils.canOpenComposerFromNavigationRouter(navigationRouter)) return;
log('SingleEmailController::openMailToLink(): address: $address');
if (address.isNotEmpty) { mailboxDashBoardController.goToComposer(
final emailAddress = EmailAddress(null, address); ComposerArguments.fromMailtoUri(
mailboxDashBoardController.goToComposer(ComposerArguments.fromEmailAddress(emailAddress)); listEmailAddress: navigationRouter.listEmailAddress,
} cc: navigationRouter.cc,
bcc: navigationRouter.bcc,
subject: navigationRouter.subject,
body: navigationRouter.body
)
);
} }
void deleteEmailPermanently(BuildContext context, PresentationEmail email) { void deleteEmailPermanently(BuildContext context, PresentationEmail email) {
@@ -30,6 +30,8 @@ class ComposerArguments extends RouterArguments {
final List<Attachment>? inlineImages; final List<Attachment>? inlineImages;
final bool? hasRequestReadReceipt; final bool? hasRequestReadReceipt;
final ScreenDisplayMode displayMode; final ScreenDisplayMode displayMode;
final List<EmailAddress>? cc;
final List<EmailAddress>? bcc;
ComposerArguments({ ComposerArguments({
this.emailActionType = EmailActionType.compose, this.emailActionType = EmailActionType.compose,
@@ -49,7 +51,9 @@ class ComposerArguments extends RouterArguments {
this.selectedIdentityId, this.selectedIdentityId,
this.inlineImages, this.inlineImages,
this.hasRequestReadReceipt, this.hasRequestReadReceipt,
this.displayMode = ScreenDisplayMode.normal this.displayMode = ScreenDisplayMode.normal,
this.cc,
this.bcc,
}); });
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) => factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
@@ -76,13 +80,20 @@ class ComposerArguments extends RouterArguments {
listEmailAddress: [emailAddress] listEmailAddress: [emailAddress]
); );
factory ComposerArguments.fromMailtoUri({List<EmailAddress>? listEmailAddress, String? subject, String? body}) => factory ComposerArguments.fromMailtoUri({
ComposerArguments( List<EmailAddress>? listEmailAddress,
emailActionType: EmailActionType.composeFromMailtoUri, String? subject,
listEmailAddress: listEmailAddress, String? body,
subject: subject, List<EmailAddress>? cc,
body: body, List<EmailAddress>? bcc
); }) => ComposerArguments(
emailActionType: EmailActionType.composeFromMailtoUri,
listEmailAddress: listEmailAddress,
subject: subject,
body: body,
cc: cc,
bcc: bcc,
);
factory ComposerArguments.editDraftEmail(PresentationEmail presentationEmail) => factory ComposerArguments.editDraftEmail(PresentationEmail presentationEmail) =>
ComposerArguments( ComposerArguments(
@@ -193,6 +204,8 @@ class ComposerArguments extends RouterArguments {
inlineImages, inlineImages,
hasRequestReadReceipt, hasRequestReadReceipt,
displayMode, displayMode,
cc,
bcc,
]; ];
ComposerArguments copyWith({ ComposerArguments copyWith({
@@ -214,6 +227,8 @@ class ComposerArguments extends RouterArguments {
List<Attachment>? inlineImages, List<Attachment>? inlineImages,
bool? hasRequestReadReceipt, bool? hasRequestReadReceipt,
ScreenDisplayMode? displayMode, ScreenDisplayMode? displayMode,
List<EmailAddress>? cc,
List<EmailAddress>? bcc,
}) { }) {
return ComposerArguments( return ComposerArguments(
emailActionType: emailActionType ?? this.emailActionType, emailActionType: emailActionType ?? this.emailActionType,
@@ -234,6 +249,8 @@ class ComposerArguments extends RouterArguments {
inlineImages: inlineImages ?? this.inlineImages, inlineImages: inlineImages ?? this.inlineImages,
hasRequestReadReceipt: hasRequestReadReceipt ?? this.hasRequestReadReceipt, hasRequestReadReceipt: hasRequestReadReceipt ?? this.hasRequestReadReceipt,
displayMode: displayMode ?? this.displayMode, displayMode: displayMode ?? this.displayMode,
cc: cc ?? this.cc,
bcc: bcc ?? this.bcc,
); );
} }
} }
@@ -460,6 +460,8 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
mailboxDashBoardController.goToComposer( mailboxDashBoardController.goToComposer(
ComposerArguments.fromMailtoUri( ComposerArguments.fromMailtoUri(
listEmailAddress: _navigationRouter?.listEmailAddress, listEmailAddress: _navigationRouter?.listEmailAddress,
cc: _navigationRouter?.cc,
bcc: _navigationRouter?.bcc,
subject: _navigationRouter?.subject, subject: _navigationRouter?.subject,
body: _navigationRouter?.body body: _navigationRouter?.body
) )
@@ -28,7 +28,7 @@ class MailtoUrlController extends ReloadableController {
if (parameters.containsKey('uri')) { if (parameters.containsKey('uri')) {
final mailtoArgument = MailtoArguments( final mailtoArgument = MailtoArguments(
session: session, session: session,
mailtoUri: parameters['uri'] mailtoUri: Uri.base.toString(),
); );
popAndPush( popAndPush(
RouteUtils.generateNavigationRoute(AppRoutes.dashboard), RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
+6
View File
@@ -21,6 +21,8 @@ class NavigationRouter with EquatableMixin {
final String? subject; final String? subject;
final String? body; final String? body;
final AccountMenuItem accountMenuItem; final AccountMenuItem accountMenuItem;
final List<EmailAddress>? cc;
final List<EmailAddress>? bcc;
NavigationRouter({ NavigationRouter({
this.emailId, this.emailId,
@@ -32,6 +34,8 @@ class NavigationRouter with EquatableMixin {
this.subject, this.subject,
this.body, this.body,
this.accountMenuItem = AccountMenuItem.none, this.accountMenuItem = AccountMenuItem.none,
this.cc,
this.bcc,
}); });
factory NavigationRouter.initial() => NavigationRouter(); factory NavigationRouter.initial() => NavigationRouter();
@@ -47,5 +51,7 @@ class NavigationRouter with EquatableMixin {
subject, subject,
body, body,
accountMenuItem, accountMenuItem,
cc,
bcc,
]; ];
} }
+85 -41
View File
@@ -25,8 +25,12 @@ abstract class RouteUtils {
static const String paramMailtoAddress = 'mailtoAddress'; static const String paramMailtoAddress = 'mailtoAddress';
static const String paramSubject = 'subject'; static const String paramSubject = 'subject';
static const String paramBody = 'body'; static const String paramBody = 'body';
static const String paramTo = 'to';
static const String paramCc = 'cc';
static const String paramBcc = 'bcc';
static const String mailtoPrefix = 'mailto:'; static const String mailtoPrefix = 'mailto';
static const String uriPrefix = 'uri';
static const String ADDRESS_SEPARATOR = ','; static const String ADDRESS_SEPARATOR = ',';
static const String INVALID_VALUE = 'invalid'; static const String INVALID_VALUE = 'invalid';
@@ -107,6 +111,8 @@ abstract class RouteUtils {
final queryParam = parameters[paramQuery]; final queryParam = parameters[paramQuery];
final routeName = parameters[paramRouteName]; final routeName = parameters[paramRouteName];
final mailtoAddress = parameters[paramMailtoAddress]; final mailtoAddress = parameters[paramMailtoAddress];
final mailtoCc = parameters[paramCc];
final mailtoBcc = parameters[paramBcc];
final subject = parameters[paramSubject]; final subject = parameters[paramSubject];
final body = parameters[paramBody]; final body = parameters[paramBody];
@@ -116,18 +122,11 @@ abstract class RouteUtils {
final dashboardType = DashboardType.values.firstWhereOrNull((type) => type.name == typeParam) ?? DashboardType.normal; final dashboardType = DashboardType.values.firstWhereOrNull((type) => type.name == typeParam) ?? DashboardType.normal;
final settingType = AccountMenuItem.values.firstWhereOrNull((type) => type.getAliasBrowser() == typeParam) ?? AccountMenuItem.none; final settingType = AccountMenuItem.values.firstWhereOrNull((type) => type.getAliasBrowser() == typeParam) ?? AccountMenuItem.none;
List<EmailAddress>? listEmailAddress; List<EmailAddress>? listEmailAddress;
if (mailtoAddress is List<String>) { List<EmailAddress>? cc;
listEmailAddress = mailtoAddress List<EmailAddress>? bcc;
.map((address) => EmailAddress( listEmailAddress = _emailAddressesFromMailtoAddress(mailtoAddress);
null, cc = _emailAddressesFromMailtoAddress(mailtoCc);
GetUtils.isEmail(address) ? address : INVALID_VALUE bcc = _emailAddressesFromMailtoAddress(mailtoBcc);
))
.toList();
} else if (mailtoAddress is String) {
listEmailAddress = [
EmailAddress(null, GetUtils.isEmail(mailtoAddress) ? mailtoAddress : INVALID_VALUE)
];
}
log('RouteUtils::parsingRouteParametersToNavigationRouter:listEmailAddress = $listEmailAddress'); log('RouteUtils::parsingRouteParametersToNavigationRouter:listEmailAddress = $listEmailAddress');
return NavigationRouter( return NavigationRouter(
emailId: emailId, emailId: emailId,
@@ -136,12 +135,27 @@ abstract class RouteUtils {
dashboardType: dashboardType, dashboardType: dashboardType,
routeName: routeName, routeName: routeName,
listEmailAddress: listEmailAddress, listEmailAddress: listEmailAddress,
cc: cc,
bcc: bcc,
subject: subject, subject: subject,
body: body, body: body,
accountMenuItem: settingType, accountMenuItem: settingType,
); );
} }
static List<EmailAddress>? _emailAddressesFromMailtoAddress(dynamic mailtoAddress) {
if (mailtoAddress is List<String>) {
return mailtoAddress
.map((address) => EmailAddress(null, address))
.toList();
} else if (mailtoAddress is String) {
return [
EmailAddress(null, mailtoAddress)
];
}
return null;
}
static void replaceBrowserHistory({required String title, required Uri url}) { static void replaceBrowserHistory({required String title, required Uri url}) {
log('RouteUtils::replaceBrowserHistory(): title: $title | url: $url'); log('RouteUtils::replaceBrowserHistory(): title: $title | url: $url');
html.window.history.replaceState(null, title, url.toString()); html.window.history.replaceState(null, title, url.toString());
@@ -152,37 +166,59 @@ abstract class RouteUtils {
final mapMailto = <String, dynamic>{ final mapMailto = <String, dynamic>{
RouteUtils.paramRouteName: AppRoutes.mailtoURL, RouteUtils.paramRouteName: AppRoutes.mailtoURL,
}; };
if (mailtoUri?.startsWith(mailtoPrefix) == true) { mailtoUri = mailtoUri == null ? null : Uri.decodeFull(mailtoUri);
final mailtoUrlDecoded = Uri.decodeFull(mailtoUri!); final parsedMailToUri = Uri.tryParse(mailtoUri ?? '');
log('RouteUtils::parseMapMailtoFromUri:mailtoUrlDecoded = $mailtoUrlDecoded');
final uri = Uri.tryParse(mailtoUrlDecoded);
if (uri == null) return mapMailto;
final mailtoAddress = uri.path; if (parsedMailToUri?.scheme == mailtoPrefix) {
final mapQueryParam = uri.queryParameters; final to = <String>{
...?parsedMailToUri?.path.split(ADDRESS_SEPARATOR),
if (mailtoAddress.contains(ADDRESS_SEPARATOR)) { ...?parsedMailToUri?.queryParameters[paramTo]?.split(ADDRESS_SEPARATOR)
final listAddress = mailtoAddress.split(ADDRESS_SEPARATOR); }.toList();
log('RouteUtils::parseMapMailtoFromUri:listAddress = $listAddress'); final cc = {
mapMailto[paramMailtoAddress] = listAddress; ...?parsedMailToUri?.queryParameters[paramCc]?.split(ADDRESS_SEPARATOR),
} else { }.toList();
log('RouteUtils::parseMapMailtoFromUri:mailtoAddress = $mailtoAddress'); final bcc = {
mapMailto[paramMailtoAddress] = mailtoAddress; ...?parsedMailToUri?.queryParameters[paramBcc]?.split(ADDRESS_SEPARATOR),
} }.toList();
if (mapQueryParam.containsKey(paramSubject)) { final subject = parsedMailToUri?.queryParameters[paramSubject];
mapMailto[paramSubject] = mapQueryParam[paramSubject]; final body = parsedMailToUri?.queryParameters[paramBody];
} mapMailto[paramMailtoAddress] = to;
if (mapQueryParam.containsKey(paramBody)) { mapMailto[paramCc] = cc;
mapMailto[paramBody] = mapQueryParam[paramBody]; mapMailto[paramBcc] = bcc;
} mapMailto[paramSubject] = subject;
} else if (mailtoUri != null) { mapMailto[paramBody] = body;
final mailtoUrlDecoded = Uri.decodeFull(mailtoUri); } else if (parsedMailToUri?.path == "/$mailtoPrefix" || parsedMailToUri?.path == "/$mailtoPrefix/") {
log('RouteUtils::parseMapMailtoFromUri:mailtoUrlDecoded = $mailtoUrlDecoded'); final to = {
mapMailto[paramMailtoAddress] = mailtoUrlDecoded; ...?parsedMailToUri?.queryParameters[uriPrefix]?.split('$mailtoPrefix:').last.split(ADDRESS_SEPARATOR),
...?parsedMailToUri?.queryParameters[paramTo]?.split(ADDRESS_SEPARATOR)
}.toList();
final cc = {
...?parsedMailToUri?.queryParameters[paramCc]?.split(ADDRESS_SEPARATOR),
}.toList();
final bcc = {
...?parsedMailToUri?.queryParameters[paramBcc]?.split(ADDRESS_SEPARATOR),
}.toList();
final subject = parsedMailToUri?.queryParameters[paramSubject];
final body = parsedMailToUri?.queryParameters[paramBody];
mapMailto[paramMailtoAddress] = to;
mapMailto[paramCc] = cc;
mapMailto[paramBcc] = bcc;
mapMailto[paramSubject] = subject;
mapMailto[paramBody] = body;
} else { } else {
mapMailto[paramMailtoAddress] = mailtoUri; mapMailto[paramMailtoAddress] = mailtoUri?.split(ADDRESS_SEPARATOR);
} }
log('RouteUtils::parseMapMailtoFromUri:mapMailto: $mapMailto');
if (mapMailto[paramMailtoAddress]?.length == 1) {
mapMailto[paramMailtoAddress] = mapMailto[paramMailtoAddress].first;
}
log('RouteUtils::parseMapMailtoFromUri:paramMailtoAddress = ${mapMailto[paramMailtoAddress]}');
log('RouteUtils::parseMapMailtoFromUri:paramCc = ${mapMailto[paramCc]}');
log('RouteUtils::parseMapMailtoFromUri:paramBcc = ${mapMailto[paramBcc]}');
log('RouteUtils::parseMapMailtoFromUri:paramSubject = ${mapMailto[paramSubject]}');
log('RouteUtils::parseMapMailtoFromUri:paramBody = ${mapMailto[paramBody]}');
return mapMailto; return mapMailto;
} }
@@ -192,4 +228,12 @@ abstract class RouteUtils {
log('RouteUtils::generateNavigationRouterFromMailtoLink:navigationRouter: $navigationRouter'); log('RouteUtils::generateNavigationRouterFromMailtoLink:navigationRouter: $navigationRouter');
return navigationRouter; return navigationRouter;
} }
static bool canOpenComposerFromNavigationRouter(NavigationRouter navigationRouter) {
return navigationRouter.listEmailAddress?.isNotEmpty == true
|| navigationRouter.cc?.isNotEmpty == true
|| navigationRouter.bcc?.isNotEmpty == true
|| navigationRouter.subject?.isNotEmpty == true
|| navigationRouter.body?.isNotEmpty == true;
}
} }
+141
View File
@@ -83,5 +83,146 @@ void main() {
expect(result[RouteUtils.paramMailtoAddress], containsAll(['test@example.com', 'test2@example.com', 'test3@example.com'])); expect(result[RouteUtils.paramMailtoAddress], containsAll(['test@example.com', 'test2@example.com', 'test3@example.com']));
expect(result[RouteUtils.paramSubject], equals('Hello')); expect(result[RouteUtils.paramSubject], equals('Hello'));
}); });
test(
'should parse a valid mailto URI encoded contains every possible parameters',
() {
// arrange
const to1 = 'to1@example.com';
const to2 = 'to2@example.com';
const to3 = 'to3@example.com';
const cc1 = 'cc1@example.com';
const cc2 = 'cc2@example.com';
const bcc1 = 'bcc1@example.com';
const bcc2 = 'bcc2@example.com';
const subject = 'Hello';
const body = 'Bye';
const mailtoSchemeUri = 'mailto:$to1,$to2'
'?to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
const mailtoPathUri = 'https://example.com/mailto'
'?uri=$to1,$to2'
'&to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
const mailtoPathWithNestedMailtoUri = 'https://example.com/mailto/'
'?uri=mailto:$to1,$to2'
'&to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
// act
final mailtoSchemeResult = RouteUtils.parseMapMailtoFromUri(
Uri.encodeFull(mailtoSchemeUri));
final mailtoPathResult = RouteUtils.parseMapMailtoFromUri(
Uri.encodeFull(mailtoPathUri));
final mailtoPathWithNestedMailtoResult = RouteUtils.parseMapMailtoFromUri(
Uri.encodeFull(mailtoPathWithNestedMailtoUri));
// assert
expect(mailtoSchemeResult, equals(mailtoPathResult));
expect(mailtoSchemeResult, equals(mailtoPathWithNestedMailtoResult));
expect(mailtoPathResult, equals(mailtoPathWithNestedMailtoResult));
expect(
mailtoSchemeResult[RouteUtils.paramMailtoAddress],
containsAll([to1, to2, to3,])
);
expect(
mailtoSchemeResult[RouteUtils.paramCc],
containsAll([cc1, cc2])
);
expect(
mailtoSchemeResult[RouteUtils.paramBcc],
containsAll([bcc1, bcc2])
);
expect(
mailtoSchemeResult[RouteUtils.paramSubject],
equals(subject)
);
expect(
mailtoSchemeResult[RouteUtils.paramBody],
equals(body)
);
});
test(
'should parse a valid mailto URI contains every possible parameters',
() {
// arrange
const to1 = 'to1@example.com';
const to2 = 'to2@example.com';
const to3 = 'to3@example.com';
const cc1 = 'cc1@example.com';
const cc2 = 'cc2@example.com';
const bcc1 = 'bcc1@example.com';
const bcc2 = 'bcc2@example.com';
const subject = 'Hello';
const body = 'Bye';
const mailtoSchemeUri = 'mailto:$to1,$to2'
'?to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
const mailtoPathUri = 'https://example.com/mailto'
'?uri=$to1,$to2'
'&to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
const mailtoPathWithNestedMailtoUri = 'https://example.com/mailto/'
'?uri=mailto:$to1,$to2'
'&to=$to2,$to3'
'&cc=$cc1,$cc2'
'&bcc=$bcc1,$bcc2'
'&subject=$subject'
'&body=$body';
// act
final mailtoSchemeResult = RouteUtils.parseMapMailtoFromUri(
mailtoSchemeUri);
final mailtoPathResult = RouteUtils.parseMapMailtoFromUri(mailtoPathUri);
final mailtoPathWithNestedMailtoResult = RouteUtils.parseMapMailtoFromUri(
mailtoPathWithNestedMailtoUri);
// assert
expect(mailtoSchemeResult, equals(mailtoPathResult));
expect(mailtoSchemeResult, equals(mailtoPathWithNestedMailtoResult));
expect(mailtoPathResult, equals(mailtoPathWithNestedMailtoResult));
expect(
mailtoSchemeResult[RouteUtils.paramMailtoAddress],
containsAll([to1, to2, to3,])
);
expect(
mailtoSchemeResult[RouteUtils.paramCc],
containsAll([cc1, cc2])
);
expect(
mailtoSchemeResult[RouteUtils.paramBcc],
containsAll([bcc1, bcc2])
);
expect(
mailtoSchemeResult[RouteUtils.paramSubject],
equals(subject)
);
expect(
mailtoSchemeResult[RouteUtils.paramBody],
equals(body)
);
});
}); });
} }