mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 221 lines 8.2 kB view raw
1import 'package:atproto/com_atproto_repo_listrecords.dart'; 2import 'package:atproto_core/atproto_core.dart'; 3import 'package:bluesky/app_bsky_actor_defs.dart'; 4import 'package:flutter_test/flutter_test.dart'; 5import 'package:lazurite/features/devtools/cubit/dev_tools_cubit.dart'; 6 7void main() { 8 group('CollectionSummary', () { 9 test('countLabel shows placeholder until count is known', () { 10 expect(const CollectionSummary('app.bsky.feed.post').countLabel, '...'); 11 expect(const CollectionSummary('app.bsky.feed.post', recordCount: 12).countLabel, '12'); 12 }); 13 }); 14 15 group('RecordInfo', () { 16 test('creates with required parameters', () { 17 const record = RecordInfo( 18 uri: 'at://did:plc:test/app.bsky.feed.post/abc123', 19 cid: 'bafyrei123', 20 value: {'text': 'Hello'}, 21 ); 22 23 expect(record.uri, 'at://did:plc:test/app.bsky.feed.post/abc123'); 24 expect(record.cid, 'bafyrei123'); 25 expect(record.value, {'text': 'Hello'}); 26 }); 27 28 test('rkey extracts last path segment', () { 29 const record = RecordInfo(uri: 'at://did:plc:test/app.bsky.feed.post/abc123', value: {}); 30 expect(record.rkey, 'abc123'); 31 }); 32 33 test('rkey returns empty string for invalid URI', () { 34 const record = RecordInfo(uri: 'invalid', value: {}); 35 expect(record.rkey, ''); 36 }); 37 38 test('atUriToLink constructs aturi.to URL', () { 39 const record = RecordInfo( 40 uri: 'at://did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.feed.post/3m6mwoadjbp2d', 41 value: {}, 42 ); 43 44 expect(record.atUriToLink, 'https://aturi.to/did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.feed.post/3m6mwoadjbp2d'); 45 }); 46 }); 47 48 group('DevToolsState', () { 49 test('initial state has default values', () { 50 const state = DevToolsState(); 51 52 expect(state.status, DevToolsStatus.initial); 53 expect(state.did, isNull); 54 expect(state.handle, isNull); 55 expect(state.repoHandle, isNull); 56 expect(state.typeaheadActors, isEmpty); 57 expect(state.isTypeaheadLoading, isFalse); 58 expect(state.collections, isEmpty); 59 expect(state.isCollectionCountsLoading, isFalse); 60 expect(state.selectedCollection, isNull); 61 expect(state.records, isNull); 62 expect(state.recordsCursor, isNull); 63 expect(state.selectedRecord, isNull); 64 expect(state.isCollectionLoading, isFalse); 65 expect(state.isRecordLoading, isFalse); 66 expect(state.errorMessage, isNull); 67 }); 68 69 test('isLoading returns true for loading statuses', () { 70 expect(const DevToolsState(status: DevToolsStatus.loading).isLoading, isTrue); 71 expect(const DevToolsState(status: DevToolsStatus.loadingMore).isLoading, isTrue); 72 expect(const DevToolsState(status: DevToolsStatus.initial).isLoading, isFalse); 73 expect(const DevToolsState(status: DevToolsStatus.repoLoaded).isLoading, isFalse); 74 }); 75 76 test('isNavigating returns true for collection or record transitions', () { 77 expect(const DevToolsState(isCollectionLoading: true).isNavigating, isTrue); 78 expect(const DevToolsState(isRecordLoading: true).isNavigating, isTrue); 79 expect(const DevToolsState().isNavigating, isFalse); 80 }); 81 82 test('hasMoreRecords returns true when cursor exists', () { 83 expect(const DevToolsState(recordsCursor: 'cursor123').hasMoreRecords, isTrue); 84 expect(const DevToolsState(recordsCursor: '').hasMoreRecords, isFalse); 85 expect(const DevToolsState().hasMoreRecords, isFalse); 86 }); 87 88 test('totalRecords returns loaded records length', () { 89 final records = [ 90 const RepoListRecordsRecord( 91 uri: AtUri('at://did:plc:test/app.bsky.feed.post/1'), 92 cid: 'cid1', 93 value: {'text': 'test'}, 94 ), 95 const RepoListRecordsRecord( 96 uri: AtUri('at://did:plc:test/app.bsky.feed.post/2'), 97 cid: 'cid2', 98 value: {'text': 'test2'}, 99 ), 100 ]; 101 102 expect(DevToolsState(records: records).totalRecords, 2); 103 expect(const DevToolsState().totalRecords, 0); 104 }); 105 106 test('totalRepoRecords returns null until all counts are known', () { 107 const state = DevToolsState( 108 collections: [CollectionSummary('app.bsky.feed.post', recordCount: 2), CollectionSummary('app.bsky.feed.like')], 109 ); 110 111 expect(state.totalRepoRecords, isNull); 112 expect( 113 const DevToolsState( 114 collections: [ 115 CollectionSummary('app.bsky.feed.post', recordCount: 2), 116 CollectionSummary('app.bsky.feed.like', recordCount: 3), 117 ], 118 ).totalRepoRecords, 119 5, 120 ); 121 }); 122 123 test('copyWith preserves unspecified values', () { 124 const state = DevToolsState( 125 status: DevToolsStatus.repoLoaded, 126 did: 'did:plc:test', 127 handle: 'test.bsky.social', 128 repoHandle: 'test.bsky.social', 129 collections: [CollectionSummary('app.bsky.feed.post')], 130 ); 131 132 final copied = state.copyWith(selectedCollection: 'app.bsky.feed.post'); 133 134 expect(copied.status, DevToolsStatus.repoLoaded); 135 expect(copied.did, 'did:plc:test'); 136 expect(copied.selectedCollection, 'app.bsky.feed.post'); 137 }); 138 139 test('copyWith can clear nullable fields', () { 140 final records = [ 141 const RepoListRecordsRecord( 142 uri: AtUri('at://did:plc:test/app.bsky.feed.post/1'), 143 cid: 'cid1', 144 value: {'text': 'test'}, 145 ), 146 ]; 147 148 final state = DevToolsState( 149 status: DevToolsStatus.recordLoaded, 150 did: 'did:plc:test', 151 handle: 'test.bsky.social', 152 repoHandle: 'test.bsky.social', 153 typeaheadActors: [const ProfileViewBasic(did: 'did:plc:test', handle: 'test.bsky.social')], 154 isTypeaheadLoading: true, 155 collections: const [CollectionSummary('app.bsky.feed.post', recordCount: 1)], 156 isCollectionCountsLoading: true, 157 selectedCollection: 'app.bsky.feed.post', 158 records: records, 159 recordsCursor: 'cursor', 160 selectedRecord: const RecordInfo(uri: 'at://did:plc:test/app.bsky.feed.post/1', value: {'text': 'full'}), 161 isCollectionLoading: true, 162 isRecordLoading: true, 163 errorMessage: 'error', 164 ); 165 166 final updated = state.copyWith( 167 did: null, 168 handle: null, 169 repoHandle: null, 170 typeaheadActors: const [], 171 isTypeaheadLoading: false, 172 isCollectionCountsLoading: false, 173 selectedCollection: null, 174 records: null, 175 recordsCursor: null, 176 selectedRecord: null, 177 isCollectionLoading: false, 178 isRecordLoading: false, 179 errorMessage: null, 180 ); 181 182 expect(updated.did, isNull); 183 expect(updated.handle, isNull); 184 expect(updated.repoHandle, isNull); 185 expect(updated.typeaheadActors, isEmpty); 186 expect(updated.isTypeaheadLoading, isFalse); 187 expect(updated.isCollectionCountsLoading, isFalse); 188 expect(updated.selectedCollection, isNull); 189 expect(updated.records, isNull); 190 expect(updated.recordsCursor, isNull); 191 expect(updated.selectedRecord, isNull); 192 expect(updated.isCollectionLoading, isFalse); 193 expect(updated.isRecordLoading, isFalse); 194 expect(updated.errorMessage, isNull); 195 }); 196 197 test('props includes all fields for equality', () { 198 const state = DevToolsState( 199 status: DevToolsStatus.repoLoaded, 200 did: 'did:plc:test', 201 handle: 'test.bsky.social', 202 repoHandle: 'test.bsky.social', 203 typeaheadActors: [ProfileViewBasic(did: 'did:plc:test', handle: 'test.bsky.social')], 204 isTypeaheadLoading: true, 205 collections: [CollectionSummary('app.bsky.feed.post', recordCount: 1)], 206 isCollectionCountsLoading: true, 207 selectedCollection: 'app.bsky.feed.post', 208 recordsCursor: 'cursor', 209 selectedRecord: RecordInfo(uri: 'at://test', value: {}), 210 isCollectionLoading: true, 211 isRecordLoading: true, 212 errorMessage: 'error', 213 ); 214 215 expect(state.props.length, 16); 216 expect(state.props, contains(DevToolsStatus.repoLoaded)); 217 expect(state.props, contains(true)); 218 expect(state.props, contains('did:plc:test')); 219 }); 220 }); 221}