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