mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 23:18:27 +02:00
Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137
This commit is contained in:
BIN
RiotTests/MatrixKitTests/Assets/test.png
Normal file
BIN
RiotTests/MatrixKitTests/Assets/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
114
RiotTests/MatrixKitTests/EncryptedAttachmentsTest.m
Normal file
114
RiotTests/MatrixKitTests/EncryptedAttachmentsTest.m
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "MXEncryptedAttachments.h"
|
||||
#import "MXEncryptedContentFile.h"
|
||||
#import "MXBase64Tools.h"
|
||||
|
||||
@interface EncryptedAttachmentsTest : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation EncryptedAttachmentsTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testDecrypt {
|
||||
NSArray *testVectors =
|
||||
@[
|
||||
@[@"", @{
|
||||
@"v": @"v1",
|
||||
@"hashes": @{
|
||||
@"sha256": @"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU"
|
||||
},
|
||||
@"key": @{
|
||||
@"alg": @"A256CTR",
|
||||
@"k": @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
@"key_ops": @[@"encrypt", @"decrypt"],
|
||||
@"kty": @"oct"
|
||||
},
|
||||
@"iv": @"AAAAAAAAAAAAAAAAAAAAAA"
|
||||
}, @""],
|
||||
@[@"5xJZTt5cQicm+9f4", @{
|
||||
@"v": @"v1",
|
||||
@"hashes": @{
|
||||
@"sha256": @"YzF08lARDdOCzJpzuSwsjTNlQc4pHxpdHcXiD/wpK6k"
|
||||
}, @"key": @{
|
||||
@"alg": @"A256CTR",
|
||||
@"k": @"__________________________________________8",
|
||||
@"key_ops": @[@"encrypt", @"decrypt"],
|
||||
@"kty": @"oct"
|
||||
}, @"iv": @"//////////8AAAAAAAAAAA"
|
||||
}, @"SGVsbG8sIFdvcmxk"],
|
||||
@[@"zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q", @{
|
||||
@"v": @"v2",
|
||||
@"hashes": @{
|
||||
@"sha256": @"IOq7/dHHB+mfHfxlRY5XMeCWEwTPmlf4cJcgrkf6fVU"
|
||||
},
|
||||
@"key": @{
|
||||
@"kty": @"oct",
|
||||
@"key_ops": @[@"encrypt",@"decrypt"],
|
||||
@"k": @"__________________________________________8",
|
||||
@"alg": @"A256CTR"
|
||||
},
|
||||
@"iv": @"//////////8AAAAAAAAAAA"
|
||||
}, @"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ"],
|
||||
@[@"tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA", @{
|
||||
@"v": @"v1",
|
||||
@"hashes": @{
|
||||
@"sha256": @"LYG/orOViuFwovJpv2YMLSsmVKwLt7pY3f8SYM7KU5E"
|
||||
},
|
||||
@"key": @{
|
||||
@"kty": @"oct",
|
||||
@"key_ops": @[@"encrypt",@"decrypt"],
|
||||
@"k": @"__________________________________________8",
|
||||
@"alg": @"A256CTR"
|
||||
},
|
||||
@"iv": @"/////////////////////w"
|
||||
}, @"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ"]
|
||||
];
|
||||
|
||||
for (NSArray *vector in testVectors) {
|
||||
NSString *inputCiphertext = vector[0];
|
||||
MXEncryptedContentFile *inputInfo = [MXEncryptedContentFile modelFromJSON:vector[1]];
|
||||
NSString *want = vector[2];
|
||||
|
||||
NSData *ctData = [[NSData alloc] initWithBase64EncodedString:[MXBase64Tools padBase64:inputCiphertext] options:0];
|
||||
NSInputStream *inputStream = [NSInputStream inputStreamWithData:ctData];
|
||||
NSOutputStream *outputStream = [NSOutputStream outputStreamToMemory];
|
||||
|
||||
[MXEncryptedAttachments decryptAttachment:inputInfo inputStream:inputStream outputStream:outputStream success:^{
|
||||
NSData *gotData = [outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
|
||||
|
||||
NSData *wantData = [[NSData alloc] initWithBase64EncodedString:[MXBase64Tools padBase64:want] options:0];
|
||||
|
||||
XCTAssertEqualObjects(wantData, gotData, "Decrypted data did not match expectation.");
|
||||
} failure:^(NSError *error) {
|
||||
XCTFail();
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
24
RiotTests/MatrixKitTests/Info.plist
Normal file
24
RiotTests/MatrixKitTests/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
24
RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h
Normal file
24
RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "MXKEventFormatter.h"
|
||||
|
||||
@interface MXKEventFormatter (Tests)
|
||||
|
||||
- (NSString*)userDisplayNameFromContentInEvent:(MXEvent*)event withMembershipFilter:(NSString *)filter;
|
||||
- (NSString*)userAvatarUrlFromContentInEvent:(MXEvent*)event withMembershipFilter:(NSString *)filter;
|
||||
|
||||
@end
|
||||
435
RiotTests/MatrixKitTests/MXKEventFormatterTests.m
Normal file
435
RiotTests/MatrixKitTests/MXKEventFormatterTests.m
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "MatrixKit.h"
|
||||
#import "MXKEventFormatter+Tests.h"
|
||||
|
||||
@import DTCoreText;
|
||||
|
||||
@interface MXEventFormatterTests : XCTestCase
|
||||
{
|
||||
MXKEventFormatter *eventFormatter;
|
||||
MXEvent *anEvent;
|
||||
CGFloat maxHeaderSize;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXEventFormatterTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
|
||||
// Create a minimal event formatter
|
||||
// Note: it may not be enough for testing all MXKEventFormatter methods
|
||||
eventFormatter = [[MXKEventFormatter alloc] initWithMatrixSession:nil];
|
||||
|
||||
eventFormatter.treatMatrixUserIdAsLink = YES;
|
||||
eventFormatter.treatMatrixRoomIdAsLink = YES;
|
||||
eventFormatter.treatMatrixRoomAliasAsLink = YES;
|
||||
eventFormatter.treatMatrixEventIdAsLink = YES;
|
||||
|
||||
anEvent = [[MXEvent alloc] init];
|
||||
anEvent.roomId = @"aRoomId";
|
||||
anEvent.eventId = @"anEventId";
|
||||
anEvent.wireType = kMXEventTypeStringRoomMessage;
|
||||
anEvent.originServerTs = (uint64_t) ([[NSDate date] timeIntervalSince1970] * 1000);
|
||||
anEvent.wireContent = @{
|
||||
@"msgtype": kMXMessageTypeText,
|
||||
@"body": @"deded",
|
||||
};
|
||||
|
||||
maxHeaderSize = ceil(eventFormatter.defaultTextFont.pointSize * 1.2);
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithHeaders
|
||||
{
|
||||
// Given HTML strings with h1/h2/h3 tags
|
||||
NSString *h1HTML = @"<h1>Large Heading</h1>";
|
||||
NSString *h2HTML = @"<h2>Smaller Heading</h2>";
|
||||
NSString *h3HTML = @"<h3>Acceptable Heading</h3>";
|
||||
|
||||
// When rendering these strings as attributed strings
|
||||
NSAttributedString *h1AttributedString = [eventFormatter renderHTMLString:h1HTML forEvent:anEvent withRoomState:nil];
|
||||
NSAttributedString *h2AttributedString = [eventFormatter renderHTMLString:h2HTML forEvent:anEvent withRoomState:nil];
|
||||
NSAttributedString *h3AttributedString = [eventFormatter renderHTMLString:h3HTML forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the h1/h2 fonts should be reduced in size to match h3.
|
||||
XCTAssertEqualObjects(h1AttributedString.string, @"Large Heading", @"The text from an H1 tag should be preserved when removing formatting.");
|
||||
XCTAssertEqualObjects(h2AttributedString.string, @"Smaller Heading", @"The text from an H2 tag should be preserved when removing formatting.");
|
||||
XCTAssertEqualObjects(h3AttributedString.string, @"Acceptable Heading", @"The text from an H3 tag should not change.");
|
||||
|
||||
[h1AttributedString enumerateAttributesInRange:NSMakeRange(0, h1AttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
UIFont *font = attributes[NSFontAttributeName];
|
||||
XCTAssertGreaterThan(font.pointSize, eventFormatter.defaultTextFont.pointSize, @"H1 tags should be larger than the default body size.");
|
||||
XCTAssertLessThanOrEqual(font.pointSize, maxHeaderSize, @"H1 tags shouldn't exceed the max header size.");
|
||||
}];
|
||||
|
||||
[h2AttributedString enumerateAttributesInRange:NSMakeRange(0, h2AttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
UIFont *font = attributes[NSFontAttributeName];
|
||||
XCTAssertGreaterThan(font.pointSize, eventFormatter.defaultTextFont.pointSize, @"H2 tags should be larger than the default body size.");
|
||||
XCTAssertLessThanOrEqual(font.pointSize, maxHeaderSize, @"H2 tags shouldn't exceed the max header size.");
|
||||
}];
|
||||
|
||||
[h3AttributedString enumerateAttributesInRange:NSMakeRange(0, h3AttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
UIFont *font = attributes[NSFontAttributeName];
|
||||
XCTAssertGreaterThan(font.pointSize, eventFormatter.defaultTextFont.pointSize, @"H3 tags should be included and be larger than the default body size.");
|
||||
XCTAssertLessThanOrEqual(font.pointSize, maxHeaderSize, @"H3 tags shouldn't exceed the max header size.");
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithPreCode
|
||||
{
|
||||
NSString *html = @"<pre><code>1\n2\n3\n4\n</code></pre>";
|
||||
NSAttributedString *as = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
NSString *a = as.string;
|
||||
|
||||
// \R : any newlines
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\R" options:0 error:0];
|
||||
XCTAssertEqual(3, [regex numberOfMatchesInString:a options:0 range:NSMakeRange(0, a.length)], "renderHTMLString must keep line break in <pre> and <code> blocks");
|
||||
|
||||
[as enumerateAttributesInRange:NSMakeRange(0, as.length) options:(0) usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
|
||||
|
||||
UIFont *font = attrs[NSFontAttributeName];
|
||||
XCTAssertEqualObjects(font.fontName, @"Menlo-Regular", "The font for <pre> and <code> should be monospace");
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithLink
|
||||
{
|
||||
// Given an HTML string with a link inside of it.
|
||||
NSString *html = @"This text contains a <a href=\"https://www.matrix.org/\">link</a>.";
|
||||
|
||||
// When rendering this string as an attributed string.
|
||||
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should contain all of the text,
|
||||
XCTAssertEqualObjects(attributedString.string, @"This text contains a link.", @"The text should be preserved when adding a link.");
|
||||
|
||||
// and the link should be added as an attachment.
|
||||
__block BOOL didFindLink = NO;
|
||||
[attributedString enumerateAttribute:NSLinkAttributeName
|
||||
inRange:NSMakeRange(0, attributedString.length)
|
||||
options:0
|
||||
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
||||
if ([value isKindOfClass:NSURL.class])
|
||||
{
|
||||
NSURL *url = (NSURL *)value;
|
||||
XCTAssertEqualObjects(url, [NSURL URLWithString:@"https://www.matrix.org/"], @"href links should be included in the text.");
|
||||
didFindLink = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertTrue(didFindLink, @"There should be a link in the attributed string.");
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithLinkInHeader
|
||||
{
|
||||
// Given HTML strings with links contained within h1/h2 tags.
|
||||
NSString *h1HTML = @"<h1><a href=\"https://www.matrix.org/\">Matrix.org</a></h1>";
|
||||
NSString *h3HTML = @"<h3><a href=\"https://www.matrix.org/\">Matrix.org</a></h3>";
|
||||
|
||||
// When rendering these strings as attributed strings.
|
||||
NSAttributedString *h1AttributedString = [eventFormatter renderHTMLString:h1HTML forEvent:anEvent withRoomState:nil];
|
||||
NSAttributedString *h3AttributedString = [eventFormatter renderHTMLString:h3HTML forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should contain all of the text,
|
||||
XCTAssertEqualObjects(h1AttributedString.string, @"Matrix.org", @"The text from an H1 tag should be preserved when removing formatting.");
|
||||
XCTAssertEqualObjects(h3AttributedString.string, @"Matrix.org", @"The text from an H3 tag should not change.");
|
||||
|
||||
// and be formatted as a header with the link added as an attachment.
|
||||
__block BOOL didFindH1Link = NO;
|
||||
[h1AttributedString enumerateAttributesInRange:NSMakeRange(0, h1AttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
UIFont *font = attributes[NSFontAttributeName];
|
||||
NSURL *url = attributes[NSLinkAttributeName];
|
||||
|
||||
if (font)
|
||||
{
|
||||
XCTAssertGreaterThan(font.pointSize, eventFormatter.defaultTextFont.pointSize, @"H1 tags should be larger than the default body size.");
|
||||
XCTAssertLessThanOrEqual(font.pointSize, maxHeaderSize, @"H1 tags shouldn't exceed the max header size.");
|
||||
}
|
||||
|
||||
if (url)
|
||||
{
|
||||
XCTAssertEqualObjects(url, [NSURL URLWithString:@"https://www.matrix.org/"], @"href links should be included in the text.");
|
||||
didFindH1Link = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
__block BOOL didFindH3Link = NO;
|
||||
[h3AttributedString enumerateAttributesInRange:NSMakeRange(0, h3AttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
UIFont *font = attributes[NSFontAttributeName];
|
||||
NSURL *url = attributes[NSLinkAttributeName];
|
||||
|
||||
if (font)
|
||||
{
|
||||
XCTAssertGreaterThan(font.pointSize, eventFormatter.defaultTextFont.pointSize, @"H3 tags should be included and be larger than the default.");
|
||||
}
|
||||
|
||||
if (url)
|
||||
{
|
||||
XCTAssertEqualObjects(url, [NSURL URLWithString:@"https://www.matrix.org/"], @"href links should be included in the text.");
|
||||
didFindH3Link = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertTrue(didFindH1Link, @"There should be a link in the sanitised attributed string.");
|
||||
XCTAssertTrue(didFindH3Link, @"There should be a link in the attributed string.");
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithIFrame
|
||||
{
|
||||
// Given an HTML string containing an unsupported iframe.
|
||||
NSString *html = @"<iframe src=\"https://www.matrix.org/\"></iframe>";
|
||||
|
||||
// When rendering this string as an attributed string.
|
||||
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should have the iframe stripped and not include any attachments.
|
||||
BOOL hasAttachment = [attributedString containsAttachmentsInRange:NSMakeRange(0, attributedString.length)];
|
||||
XCTAssertFalse(hasAttachment, @"iFrame attachments should be removed as they're not included in the allowedHTMLTags array.");
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithMXReply
|
||||
{
|
||||
// Given an HTML string representing a matrix reply.
|
||||
NSString *html = @"<mx-reply><blockquote><a href=\"https://matrix.to/#/someroom/someevent\">In reply to</a> <a href=\"https://matrix.to/#/@alice:matrix.org\">@alice:matrix.org</a><br>Original message.</blockquote></mx-reply>This is a reply.";
|
||||
|
||||
// When rendering this string as an attributed string.
|
||||
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should contain all of the text,
|
||||
NSString *plainString = [attributedString.string stringByReplacingOccurrencesOfString:@"\U00002028" withString:@"\n"];
|
||||
XCTAssertEqualObjects(plainString, @"In reply to @alice:matrix.org\nOriginal message.\nThis is a reply.",
|
||||
@"The reply string should include who the original message was from, what they said, and the reply itself.");
|
||||
|
||||
// and format the author and original message inside of a quotation block.
|
||||
__block BOOL didTestReplyText = NO;
|
||||
__block BOOL didTestQuoteBlock = NO;
|
||||
[attributedString enumerateAttributesInRange:NSMakeRange(0, attributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
NSString *substring = [attributedString attributedSubstringFromRange:range].string;
|
||||
|
||||
if ([substring isEqualToString:@"This is a reply."])
|
||||
{
|
||||
XCTAssertNil(attributes[DTTextBlocksAttribute], @"The reply text should not appear within a block");
|
||||
didTestReplyText = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
XCTAssertNotNil(attributes[DTTextBlocksAttribute], @"The rest of the string should be within a block");
|
||||
didTestQuoteBlock = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertTrue(didTestReplyText && didTestQuoteBlock, @"Both a quote and a reply should be in the attributed string.");
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithMXReplyQuotingInvalidMessage
|
||||
{
|
||||
// Given an HTML string representing a matrix reply where the original message has invalid HTML.
|
||||
NSString *html = @"<mx-reply><blockquote><a href=\"https://matrix.to/#/someroom/someevent\">In reply to</a> <a href=\"https://matrix.to/#/@alice:matrix.org\">@alice:matrix.org</a><br><h1>Heading with <badtag>invalid</badtag> content</h1></blockquote></mx-reply>This is a reply.";
|
||||
|
||||
// When rendering this string as an attributed string.
|
||||
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should contain all of the text,
|
||||
NSString *plainString = [attributedString.string stringByReplacingOccurrencesOfString:@"\U00002028" withString:@"\n"];
|
||||
XCTAssertEqualObjects(plainString, @"In reply to @alice:matrix.org\nHeading with invalid content\nThis is a reply.",
|
||||
@"The reply string should include who the original message was from, what they said, and the reply itself.");
|
||||
|
||||
// and format the author and original message inside of a quotation block. This check
|
||||
// is to catch any incorrectness in the sanitizing where the original message becomes
|
||||
// indented but is missing the block quote mark attribute.
|
||||
__block BOOL didTestReplyText = NO;
|
||||
__block BOOL didTestQuoteBlock = NO;
|
||||
[attributedString enumerateAttributesInRange:NSMakeRange(0, attributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attributes, NSRange range, BOOL * _Nonnull stop) {
|
||||
|
||||
NSString *substring = [attributedString attributedSubstringFromRange:range].string;
|
||||
|
||||
if ([substring isEqualToString:@"This is a reply."])
|
||||
{
|
||||
XCTAssertNil(attributes[DTTextBlocksAttribute], @"The reply text should not appear within a block");
|
||||
didTestReplyText = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
XCTAssertNotNil(attributes[DTTextBlocksAttribute], @"The rest of the string should be within a block");
|
||||
XCTAssertNotNil(attributes[kMXKToolsBlockquoteMarkAttribute], @"The block should have the blockquote style applied");
|
||||
didTestQuoteBlock = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertTrue(didTestReplyText && didTestQuoteBlock, @"Both a quote and a reply should be in the attributed string.");
|
||||
}
|
||||
|
||||
- (void)testRenderHTMLStringWithImageHandler
|
||||
{
|
||||
MXWeakify(self);
|
||||
|
||||
// Given an HTML string that contains an image tag inline.
|
||||
NSURL *imageURL = [NSURL URLWithString:@"https://matrix.org/images/matrix-logo.svg"];
|
||||
NSString *html = [NSString stringWithFormat:@"Look at this logo: <img src=\"%@\"> Very nice.", imageURL.absoluteString];
|
||||
|
||||
// When rendering this string as an attributed string using an appropriate image handler block.
|
||||
eventFormatter.allowedHTMLTags = [eventFormatter.allowedHTMLTags arrayByAddingObject:@"img"];
|
||||
eventFormatter.htmlImageHandler = ^NSURL *(NSString *sourceURL, CGFloat width, CGFloat height) {
|
||||
MXStrongifyAndReturnValueIfNil(self, nil);
|
||||
|
||||
// Replace the image URL with one from the tests bundle
|
||||
NSBundle *bundle = [NSBundle bundleForClass:self.class];;
|
||||
return [bundle URLForResource:@"test" withExtension:@"png"];
|
||||
};
|
||||
NSAttributedString *attributedString = [eventFormatter renderHTMLString:html forEvent:anEvent withRoomState:nil];
|
||||
|
||||
// Then the attributed string should contain all of the text,
|
||||
NSString *plainString = [attributedString.string stringByReplacingOccurrencesOfString:@"\U0000fffc" withString:@""];
|
||||
XCTAssertEqualObjects(plainString, @"Look at this logo: Very nice.", @"The string should include the original text.");
|
||||
|
||||
// and have the image included as an attachment.
|
||||
__block BOOL hasImageAttachment = NO;
|
||||
[attributedString enumerateAttribute:NSAttachmentAttributeName
|
||||
inRange:NSMakeRange(0, attributedString.length)
|
||||
options:0
|
||||
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
||||
if ([value image])
|
||||
{
|
||||
hasImageAttachment = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertTrue(hasImageAttachment, @"There should be an attachment that contains the image.");
|
||||
}
|
||||
|
||||
- (void)testMarkdownFormatting
|
||||
{
|
||||
NSString *html = [eventFormatter htmlStringFromMarkdownString:@"Line One.\nLine Two."];
|
||||
|
||||
BOOL hardBreakExists = [html rangeOfString:@"<br />"].location != NSNotFound;
|
||||
BOOL openParagraphExists = [html rangeOfString:@"<p>"].location != NSNotFound;
|
||||
BOOL closeParagraphExists = [html rangeOfString:@"</p>"].location != NSNotFound;
|
||||
|
||||
// Check for some known error cases
|
||||
XCTAssert(hardBreakExists, "The soft break (\\n) must be converted to a hard break (<br />).");
|
||||
XCTAssert(!openParagraphExists && !closeParagraphExists, "The html must not contain any opening or closing paragraph tags.");
|
||||
}
|
||||
|
||||
#pragma mark - Links
|
||||
|
||||
- (void)testRoomAliasLink
|
||||
{
|
||||
NSString *s = @"Matrix HQ room is at #matrix:matrix.org.";
|
||||
NSAttributedString *as = [eventFormatter renderString:s forEvent:anEvent];
|
||||
|
||||
NSRange linkRange = [s rangeOfString:@"#matrix:matrix.org"];
|
||||
|
||||
__block NSUInteger ranges = 0;
|
||||
__block BOOL linkCreated = NO;
|
||||
|
||||
[as enumerateAttributesInRange:NSMakeRange(0, as.length) options:(0) usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
|
||||
|
||||
ranges++;
|
||||
|
||||
if (NSEqualRanges(linkRange, range))
|
||||
{
|
||||
linkCreated = (attrs[NSLinkAttributeName] != nil);
|
||||
}
|
||||
}];
|
||||
|
||||
XCTAssertEqual(ranges, 3, @"A sub-component must have been found");
|
||||
XCTAssert(linkCreated, @"Link not created as expected: %@", as);
|
||||
}
|
||||
|
||||
- (void)testLinkWithRoomAliasLink
|
||||
{
|
||||
NSString *s = @"Matrix HQ room is at https://matrix.to/#/room/#matrix:matrix.org.";
|
||||
NSAttributedString *as = [eventFormatter renderString:s forEvent:anEvent];
|
||||
|
||||
__block NSUInteger ranges = 0;
|
||||
|
||||
[as enumerateAttributesInRange:NSMakeRange(0, as.length) options:(0) usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
|
||||
|
||||
ranges++;
|
||||
}];
|
||||
|
||||
XCTAssertEqual(ranges, 1, @"There should be no link in this case. We let the UI manage the link");
|
||||
}
|
||||
|
||||
#pragma mark - Event sender/target info
|
||||
|
||||
- (void)testUserDisplayNameFromEventContent {
|
||||
MXEvent *event = [self eventFromJSON:@"{\"sender\":\"@alice:matrix.org\",\"content\":{\"displayname\":\"bob\",\"membership\":\"invite\"},\"origin_server_ts\":1616488993287,\"state_key\":\"@bob:matrix.org\",\"room_id\":\"!foofoofoofoofoofoo:matrix.org\",\"event_id\":\"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do\",\"type\":\"m.room.member\",\"unsigned\":{}}"];
|
||||
XCTAssertEqualObjects([eventFormatter userDisplayNameFromContentInEvent:event withMembershipFilter:nil], @"bob");
|
||||
XCTAssertEqualObjects([eventFormatter userDisplayNameFromContentInEvent:event withMembershipFilter:@"invite"], @"bob");
|
||||
XCTAssertEqualObjects([eventFormatter userDisplayNameFromContentInEvent:event withMembershipFilter:@"join"], nil);
|
||||
}
|
||||
|
||||
- (void)testUserDisplayNameFromNonMembershipEventContent {
|
||||
MXEvent *event = [self eventFromJSON:@"{\"sender\":\"@alice:matrix.org\",\"content\":{\"ciphertext\":\"foo\",\"sender_key\":\"bar\",\"device_id\":\"foobar\",\"algorithm\":\"m.megolm.v1.aes-sha2\"}},\"origin_server_ts\":1616488993287,\"state_key\":\"@bob:matrix.org\",\"room_id\":\"!foofoofoofoofoofoo:matrix.org\",\"event_id\":\"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do\",\"type\":\"m.room.encrypted\",\"unsigned\":{}}"];
|
||||
XCTAssertEqualObjects([eventFormatter userDisplayNameFromContentInEvent:event withMembershipFilter:nil], nil);
|
||||
XCTAssertEqualObjects([eventFormatter userDisplayNameFromContentInEvent:event withMembershipFilter:@"join"], nil);
|
||||
}
|
||||
|
||||
- (void)testUserAvatarUrlFromEventContent {
|
||||
MXEvent *event = [self eventFromJSON:@"{\"sender\":\"@alice:matrix.org\",\"content\":{\"displayname\":\"bob\",\"avatar_url\":\"mxc://foo.bar\",\"membership\":\"join\"},\"origin_server_ts\":1616488993287,\"state_key\":\"@bob:matrix.org\",\"room_id\":\"!foofoofoofoofoofoo:matrix.org\",\"event_id\":\"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do\",\"type\":\"m.room.member\",\"unsigned\":{}}"];
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:nil], @"mxc://foo.bar");
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:@"invite"], nil);
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:@"join"], @"mxc://foo.bar");
|
||||
}
|
||||
|
||||
- (void)testUserAvatarUrlFromEventWithNonMXCAvatarUrlContent {
|
||||
MXEvent *event = [self eventFromJSON:@"{\"sender\":\"@alice:matrix.org\",\"content\":{\"displayname\":\"bob\",\"avatar_url\":\"http://foo.bar\",\"membership\":\"join\"},\"origin_server_ts\":1616488993287,\"state_key\":\"@bob:matrix.org\",\"room_id\":\"!foofoofoofoofoofoo:matrix.org\",\"event_id\":\"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do\",\"type\":\"m.room.member\",\"unsigned\":{}}"];
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:nil], nil);
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:@"invite"], nil);
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:@"join"], nil);
|
||||
}
|
||||
|
||||
- (void)testUserAvatarUrlFromNonMembershipEventContent {
|
||||
MXEvent *event = [self eventFromJSON:@"{\"sender\":\"@alice:matrix.org\",\"content\":{\"ciphertext\":\"foo\",\"sender_key\":\"bar\",\"device_id\":\"foobar\",\"algorithm\":\"m.megolm.v1.aes-sha2\"}},\"origin_server_ts\":1616488993287,\"state_key\":\"@bob:matrix.org\",\"room_id\":\"!foofoofoofoofoofoo:matrix.org\",\"event_id\":\"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do\",\"type\":\"m.room.encrypted\",\"unsigned\":{}}"];
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:nil], nil);
|
||||
XCTAssertEqualObjects([eventFormatter userAvatarUrlFromContentInEvent:event withMembershipFilter:@"join"], nil);
|
||||
}
|
||||
|
||||
- (MXEvent *)eventFromJSON:(NSString *)json {
|
||||
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
return [MXEvent modelFromJSON:dict];
|
||||
}
|
||||
|
||||
@end
|
||||
27
RiotTests/MatrixKitTests/MXKRoomDataSource+Tests.h
Normal file
27
RiotTests/MatrixKitTests/MXKRoomDataSource+Tests.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "MXKRoomDataSource.h"
|
||||
|
||||
@interface MXKRoomDataSource (Tests)
|
||||
|
||||
- (NSArray<id<MXKRoomBubbleCellDataStoring>> *)getBubbles;
|
||||
- (void)replaceBubbles:(NSArray<id<MXKRoomBubbleCellDataStoring>> *)newBubbles;
|
||||
|
||||
- (void)queueEventForProcessing:(MXEvent*)event withRoomState:(MXRoomState*)roomState direction:(MXTimelineDirection)direction;
|
||||
- (void)processQueuedEvents:(void (^)(NSUInteger addedHistoryCellNb, NSUInteger addedLiveCellNb))onComplete;
|
||||
|
||||
@end
|
||||
29
RiotTests/MatrixKitTests/MXKRoomDataSource+Tests.m
Normal file
29
RiotTests/MatrixKitTests/MXKRoomDataSource+Tests.m
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "MXKRoomDataSource+Tests.h"
|
||||
|
||||
@implementation MXKRoomDataSource (Tests)
|
||||
|
||||
- (NSArray<id<MXKRoomBubbleCellDataStoring>> *)getBubbles {
|
||||
return bubbles;
|
||||
}
|
||||
|
||||
- (void)replaceBubbles:(NSArray<id<MXKRoomBubbleCellDataStoring>> *)newBubbles {
|
||||
bubbles = [NSMutableArray arrayWithArray:newBubbles];
|
||||
}
|
||||
|
||||
@end
|
||||
155
RiotTests/MatrixKitTests/MXKRoomDataSourceTests.swift
Normal file
155
RiotTests/MatrixKitTests/MXKRoomDataSourceTests.swift
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
@testable import MatrixKit
|
||||
|
||||
class MXKRoomDataSourceTests: XCTestCase {
|
||||
|
||||
// MARK: - Destruction tests
|
||||
|
||||
func testDestroyRemovesAllBubbles() {
|
||||
let dataSource = StubMXKRoomDataSource()
|
||||
dataSource.destroy()
|
||||
XCTAssert(dataSource.getBubbles()?.isEmpty != false)
|
||||
}
|
||||
|
||||
func testDestroyDeallocatesAllBubbles() throws {
|
||||
let dataSource = StubMXKRoomDataSource()
|
||||
weak var first = try XCTUnwrap(dataSource.getBubbles()?.first)
|
||||
weak var last = try XCTUnwrap(dataSource.getBubbles()?.last)
|
||||
dataSource.destroy()
|
||||
XCTAssertNil(first)
|
||||
XCTAssertNil(last)
|
||||
}
|
||||
|
||||
// MARK: - Collapsing tests
|
||||
|
||||
func testCollapseBubblesWhenProcessingTogether() throws {
|
||||
let dataSource = try FakeMXKRoomDataSource.make()
|
||||
try dataSource.queueEvent1()
|
||||
try dataSource.queueEvent2()
|
||||
awaitEventProcessing(for: dataSource)
|
||||
dataSource.verifyCollapsedEvents(2)
|
||||
}
|
||||
|
||||
func testCollapseBubblesWhenProcessingAlone() throws {
|
||||
let dataSource = try FakeMXKRoomDataSource.make()
|
||||
try dataSource.queueEvent1()
|
||||
awaitEventProcessing(for: dataSource)
|
||||
try dataSource.queueEvent2()
|
||||
awaitEventProcessing(for: dataSource)
|
||||
dataSource.verifyCollapsedEvents(2)
|
||||
}
|
||||
|
||||
private func awaitEventProcessing(for dataSource: MXKRoomDataSource) {
|
||||
let e = expectation(description: "The wai-ai-ting is the hardest part")
|
||||
dataSource.processQueuedEvents { _, _ in
|
||||
e.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 2) { error in
|
||||
XCTAssertNil(error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Test doubles
|
||||
|
||||
private final class StubMXKRoomDataSource: MXKRoomDataSource {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
let data1 = MXKRoomBubbleCellData()
|
||||
let data2 = MXKRoomBubbleCellData()
|
||||
let data3 = MXKRoomBubbleCellData()
|
||||
|
||||
data1.nextCollapsableCellData = data2
|
||||
data2.prevCollapsableCellData = data1
|
||||
data2.nextCollapsableCellData = data3
|
||||
data3.prevCollapsableCellData = data2
|
||||
|
||||
replaceBubbles([data1, data2, data3])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class FakeMXKRoomDataSource: MXKRoomDataSource {
|
||||
|
||||
class func make() throws -> FakeMXKRoomDataSource {
|
||||
let dataSource = try XCTUnwrap(FakeMXKRoomDataSource(roomId: "!foofoofoofoofoofoo:matrix.org", andMatrixSession: nil))
|
||||
dataSource.registerCellDataClass(CollapsibleBubbleCellData.self, forCellIdentifier: kMXKRoomBubbleCellDataIdentifier)
|
||||
dataSource.eventFormatter = CountingEventFormatter(matrixSession: nil)
|
||||
return dataSource
|
||||
}
|
||||
|
||||
override var state: MXKDataSourceState {
|
||||
MXKDataSourceStateReady
|
||||
}
|
||||
|
||||
override var roomState: MXRoomState! {
|
||||
nil
|
||||
}
|
||||
|
||||
func queueEvent1() throws {
|
||||
try queueEvent(json: #"{"sender":"@alice:matrix.org","content":{"displayname":"bob","membership":"invite"},"origin_server_ts":1616488993287,"state_key":"@bob:matrix.org","room_id":"!foofoofoofoofoofoo:matrix.org","event_id":"$lGK3budX5w009ErtQwE9ZFhwyUUAV9DqEN5yb2fI4Do","type":"m.room.member","unsigned":{"age":1204610,"prev_sender":"@alice:matrix.org","prev_content":{"membership":"leave"},"replaces_state":"$9mQ6RtscXqHCxWqOElI-eP_kwpkuPd2Czm3UHviGoyE"}}"#)
|
||||
}
|
||||
|
||||
func queueEvent2() throws {
|
||||
try queueEvent(json: #"{"sender":"@alice:matrix.org","content":{"displayname":"john","membership":"invite"},"origin_server_ts":1616488967295,"state_key":"@john:matrix.org","room_id":"!foofoofoofoofoofoo:matrix.org","event_id":"$-00slfAluxVTP2VWytgDThTmh3nLd0WJD6gzBo2scJM","type":"m.room.member","unsigned":{"age":1712006,"prev_sender":"@alice:matrix.org","prev_content":{"membership":"leave"},"replaces_state":"$NRNkCMKeKK5NtTfWkMfTlMr5Ygw60Q2CQYnJNkbzyrs"}}"#)
|
||||
}
|
||||
|
||||
private func queueEvent(json: String) throws {
|
||||
let data = try XCTUnwrap(json.data(using: .utf8))
|
||||
let dict = try XCTUnwrap((try JSONSerialization.jsonObject(with: data, options: [])) as? [AnyHashable: Any])
|
||||
let event = MXEvent(fromJSON: dict)
|
||||
queueEvent(forProcessing: event, with: nil, direction: __MXTimelineDirectionForwards)
|
||||
}
|
||||
|
||||
func verifyCollapsedEvents(_ number: Int) {
|
||||
let message = getBubbles()?.first?.collapsedAttributedTextMessage.string
|
||||
XCTAssertEqual(message, "\(number)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class CollapsibleBubbleCellData: MXKRoomBubbleCellData {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
required init!(event: MXEvent!, andRoomState roomState: MXRoomState!, andRoomDataSource roomDataSource: MXKRoomDataSource!) {
|
||||
super.init(event: event, andRoomState: roomState, andRoomDataSource: roomDataSource)
|
||||
collapsable = true
|
||||
}
|
||||
|
||||
override func collapse(with cellData: MXKRoomBubbleCellDataStoring!) -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class CountingEventFormatter: MXKEventFormatter {
|
||||
|
||||
override func attributedString(from events: [MXEvent]!, with roomState: MXRoomState!, error: UnsafeMutablePointer<MXKEventFormatterError>!) -> NSAttributedString! {
|
||||
NSAttributedString(string: "\(events.count)")
|
||||
}
|
||||
|
||||
}
|
||||
1
RiotTests/MatrixKitTests/UTI/Files/Text.txt
Normal file
1
RiotTests/MatrixKitTests/UTI/Files/Text.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
99
RiotTests/MatrixKitTests/UTI/MXKUTITests.swift
Normal file
99
RiotTests/MatrixKitTests/UTI/MXKUTITests.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import MatrixKit
|
||||
|
||||
import MobileCoreServices
|
||||
|
||||
class MXKUTITests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testUTIFromMimeType() {
|
||||
guard let uti = MXKUTI(mimeType: "application/pdf") else {
|
||||
XCTFail("uti should not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
let fileExtension = uti.fileExtension?.lowercased() ?? ""
|
||||
|
||||
XCTAssertTrue(uti.isFile)
|
||||
XCTAssertEqual(fileExtension, "pdf")
|
||||
}
|
||||
|
||||
func testUTIFromFileExtension() {
|
||||
let uti = MXKUTI(fileExtension: "pdf")
|
||||
|
||||
let fileExtension = uti.fileExtension?.lowercased() ?? ""
|
||||
let mimeType = uti.mimeType ?? ""
|
||||
|
||||
XCTAssertTrue(uti.isFile)
|
||||
XCTAssertEqual(fileExtension, "pdf")
|
||||
XCTAssertEqual(mimeType, "application/pdf")
|
||||
}
|
||||
|
||||
func testUTIFromLocalFileURLLoadingResourceValues() {
|
||||
|
||||
let bundle = Bundle(for: type(of: self))
|
||||
|
||||
guard let localFileURL = bundle.url(forResource: "Text", withExtension: "txt") else {
|
||||
XCTFail("localFileURL should not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard let uti = MXKUTI(localFileURL: localFileURL) else {
|
||||
XCTFail("uti should not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
let fileExtension = uti.fileExtension?.lowercased() ?? ""
|
||||
let mimeType = uti.mimeType ?? ""
|
||||
|
||||
XCTAssertTrue(uti.isFile)
|
||||
XCTAssertEqual(fileExtension, "txt")
|
||||
XCTAssertEqual(mimeType, "text/plain")
|
||||
}
|
||||
|
||||
func testUTIFromLocalFileURL() {
|
||||
|
||||
let bundle = Bundle(for: type(of: self))
|
||||
|
||||
guard let localFileURL = bundle.url(forResource: "Text", withExtension: "txt") else {
|
||||
XCTFail("localFileURL should not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard let uti = MXKUTI(localFileURL: localFileURL, loadResourceValues: false) else {
|
||||
XCTFail("uti should not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
let fileExtension = uti.fileExtension?.lowercased() ?? ""
|
||||
let mimeType = uti.mimeType ?? ""
|
||||
|
||||
XCTAssertTrue(uti.isFile)
|
||||
XCTAssertEqual(fileExtension, "txt")
|
||||
XCTAssertEqual(mimeType, "text/plain")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user