Skip to content

Commit 7edc5b8

Browse files
CopilotNateowami
andauthored
Optimize progress service tests by eliminating inefficient deepEqual usage (#3534)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Nateowami <[email protected]>
1 parent 05ccd4c commit 7edc5b8

File tree

1 file changed

+62
-37
lines changed

1 file changed

+62
-37
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/shared/progress-service/progress.service.spec.ts

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createTestProjectProfile } from 'realtime-server/lib/esm/scriptureforge
44
import { TextData } from 'realtime-server/lib/esm/scriptureforge/models/text-data';
55
import { Chapter, TextInfo } from 'realtime-server/lib/esm/scriptureforge/models/text-info';
66
import { BehaviorSubject, of } from 'rxjs';
7-
import { anything, deepEqual, instance, mock, when } from 'ts-mockito';
7+
import { anything, instance, mock, when } from 'ts-mockito';
88
import { ActivatedProjectService } from 'xforge-common/activated-project.service';
99
import { NoticeService } from 'xforge-common/notice.service';
1010
import { TestOnlineStatusModule } from 'xforge-common/test-online-status.module';
@@ -82,28 +82,12 @@ describe('progress service', () => {
8282
it('updates total progress when chapter content changes', fakeAsync(async () => {
8383
const env = new TestEnvironment();
8484
const changeEvent = new BehaviorSubject({});
85-
when(mockSFProjectService.getText(deepEqual(new TextDocId('project01', 1, 2, 'target')))).thenCall(() => {
86-
return {
87-
getSegmentCount: () => {
88-
return { translated: 12, blank: 2 };
89-
},
90-
getNonEmptyVerses: () => env.createVerses(12),
91-
changes$: changeEvent
92-
};
93-
});
85+
env.setTextData('project01', 1, 2, 'target', 12, 2, changeEvent);
9486

9587
tick();
9688

9789
// mock a change
98-
when(mockSFProjectService.getText(deepEqual(new TextDocId('project01', 1, 2, 'target')))).thenCall(() => {
99-
return {
100-
getSegmentCount: () => {
101-
return { translated: 13, blank: 1 };
102-
},
103-
getNonEmptyVerses: () => env.createVerses(13),
104-
changes$: changeEvent
105-
};
106-
});
90+
env.setTextData('project01', 1, 2, 'target', 13, 1, changeEvent);
10791

10892
const originalProgress = env.service.overallProgress.translated;
10993
tick(1000); // wait for the throttle time
@@ -155,9 +139,9 @@ describe('progress service', () => {
155139

156140
it('cannot train suggestions if no source permission', fakeAsync(async () => {
157141
const env = new TestEnvironment();
158-
when(
159-
mockPermissionService.canAccessText(deepEqual(new TextDocId('sourceId', anything(), anything(), 'target')))
160-
).thenResolve(false);
142+
when(mockPermissionService.canAccessText(anything())).thenCall((textDocId: TextDocId) => {
143+
return Promise.resolve(textDocId.projectId !== 'sourceId');
144+
});
161145
tick();
162146

163147
expect(env.service.canTrainSuggestions).toBeFalsy();
@@ -188,6 +172,9 @@ class TestEnvironment {
188172
readonly mockProject = mock(SFProjectProfileDoc);
189173
readonly project$ = new BehaviorSubject(instance(this.mockProject));
190174

175+
// Store all text data in a single map to avoid repeated deepEqual calls
176+
private readonly allTextData = new Map<string, { translated: number; blank: number }>();
177+
191178
constructor(
192179
private readonly translatedSegments: number = 1000,
193180
private readonly blankSegments: number = 500
@@ -218,37 +205,75 @@ class TestEnvironment {
218205
id: 'project02',
219206
remoteChanges$: new BehaviorSubject([])
220207
} as unknown as SFProjectProfileDoc);
221-
this.setUpGetText('project02', 0, 1000);
208+
this.populateTextData('project02', 0, 1000);
209+
210+
this.populateTextData('sourceId', this.translatedSegments, this.blankSegments);
211+
this.populateTextData('project01', this.translatedSegments, this.blankSegments);
212+
213+
// Set up a single mock for getText that handles all TextDocId instances
214+
when(mockSFProjectService.getText(anything())).thenCall((textDocId: TextDocId) => {
215+
const key = `${textDocId.projectId}:${textDocId.bookNum}:${textDocId.chapterNum}:${textDocId.textType}`;
216+
const data = this.allTextData.get(key);
217+
const changeKey = `${key}:changes`;
218+
const customChanges = (this.allTextData as any).get(changeKey);
219+
220+
if (data != null) {
221+
return {
222+
getSegmentCount: () => {
223+
return { translated: data.translated, blank: data.blank };
224+
},
225+
getNonEmptyVerses: () => this.createVerses(data.translated),
226+
changes$: customChanges ?? of({} as TextData)
227+
};
228+
}
222229

223-
this.setUpGetText('sourceId', this.translatedSegments, this.blankSegments);
224-
this.setUpGetText('project01', this.translatedSegments, this.blankSegments);
230+
// Return a default value if not found
231+
return {
232+
getSegmentCount: () => {
233+
return { translated: 0, blank: 0 };
234+
},
235+
getNonEmptyVerses: () => [],
236+
changes$: of({} as TextData)
237+
};
238+
});
225239

226240
this.service = TestBed.inject(ProgressService);
227241
}
228242

229-
setUpGetText(projectId: string, translatedSegments: number, blankSegments: number): void {
243+
private populateTextData(projectId: string, translatedSegments: number, blankSegments: number): void {
230244
for (let book = 1; book <= this.numBooks; book++) {
231245
for (let chapter = 0; chapter < this.numChapters; chapter++) {
232246
const translated = translatedSegments >= 9 ? 9 : translatedSegments;
233247
translatedSegments -= translated;
234248
const blank = blankSegments >= 5 ? 5 : blankSegments;
235249
blankSegments -= blank;
236250

237-
when(mockSFProjectService.getText(deepEqual(new TextDocId(projectId, book, chapter, 'target')))).thenCall(
238-
() => {
239-
return {
240-
getSegmentCount: () => {
241-
return { translated, blank };
242-
},
243-
getNonEmptyVerses: () => this.createVerses(translated),
244-
changes$: of({} as TextData)
245-
};
246-
}
247-
);
251+
const key = `${projectId}:${book}:${chapter}:target`;
252+
this.allTextData.set(key, { translated, blank });
248253
}
249254
}
250255
}
251256

257+
setTextData(
258+
projectId: string,
259+
book: number,
260+
chapter: number,
261+
textType: string,
262+
translated: number,
263+
blank: number,
264+
changes$?: BehaviorSubject<any>
265+
): void {
266+
const key = `${projectId}:${book}:${chapter}:${textType}`;
267+
this.allTextData.set(key, { translated, blank });
268+
269+
// If a custom changes$ observable is provided, we need to store it
270+
// so the mock can return it
271+
if (changes$ != null) {
272+
const changeKey = `${key}:changes`;
273+
(this.allTextData as any).set(changeKey, changes$);
274+
}
275+
}
276+
252277
createVerses(num: number): string[] {
253278
let count = 0;
254279
return Array.from({ length: num }, () => 'verse' + ++count);

0 commit comments

Comments
 (0)