|
|
@@ -21,23 +21,30 @@ describe('ReadingExerciseComponent', () => {
|
|
|
content:'apple river',
|
|
|
createOptions:{ tpl:'template', params:{ wordList:['apple'], count:120, stage:'小学', level:'三年级', type:'记叙文', theme:'校园' } },
|
|
|
};
|
|
|
- const questions: ReadingQuestion[] = [
|
|
|
- { objectId:'question-1', type:'reading-item', parent:{ __type:'Pointer', className:'SurveyItem', objectId:'article-1' }, index:0, title:'Question 1', options:[{ value:'A', check:true }, { value:'B', check:false }] },
|
|
|
- { objectId:'question-2', type:'reading-item', parent:{ __type:'Pointer', className:'SurveyItem', objectId:'article-1' }, index:1, title:'Question 2', options:[{ value:'C', check:true }, { value:'D', check:false }] },
|
|
|
- ];
|
|
|
+ const questions: ReadingQuestion[] = Array.from({ length: 5 }, (_, index) => ({
|
|
|
+ objectId:`question-${index + 1}`, type:'reading-item', parent:{ __type:'Pointer', className:'SurveyItem', objectId:'article-1' },
|
|
|
+ index, title:`Question ${index + 1}`, options:[
|
|
|
+ { value:'A', check:index===0 }, { value:'B', check:false }, { value:'C', check:index!==0 }, { value:'D', check:false },
|
|
|
+ ],
|
|
|
+ }));
|
|
|
const savedLog: ReadingLog = {
|
|
|
objectId:'log-1', surveyItem:{ __type:'Pointer', className:'SurveyItem', objectId:'article-1' },
|
|
|
- answer:{ 'question-1':'A', 'question-2':'D' }, right:1, wrong:1, grade:50, type:'exam',
|
|
|
+ answer:{ 'question-1':'A', 'question-2':'D' }, right:1, wrong:4, grade:20, type:'exam',
|
|
|
};
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
- reading = jasmine.createSpyObj<EnglishReadingService>('EnglishReadingService', ['saveLog']);
|
|
|
+ reading = jasmine.createSpyObj<EnglishReadingService>('EnglishReadingService', ['saveLog', 'findArticles', 'createArticle', 'latestLog', 'getQuestions', 'createQuestions', 'supplyMissingWords']);
|
|
|
router = jasmine.createSpyObj<Router>('Router', ['navigate', 'navigateByUrl']);
|
|
|
router.navigate.and.resolveTo(true);
|
|
|
router.navigateByUrl.and.resolveTo(true);
|
|
|
platform = jasmine.createSpyObj<PlatformService>('PlatformService', ['toast', 'confirm']);
|
|
|
platform.confirm.and.resolveTo(true);
|
|
|
- const cloud = jasmine.createSpyObj<ReadingCloudService>('ReadingCloudService', ['ensureUser']);
|
|
|
+ const cloud = jasmine.createSpyObj<ReadingCloudService>('ReadingCloudService', ['ensureUser', 'get']);
|
|
|
+ reading.findArticles.and.resolveTo([]);
|
|
|
+ reading.latestLog.and.resolveTo(null);
|
|
|
+ reading.getQuestions.and.resolveTo(questions);
|
|
|
+ reading.createQuestions.and.resolveTo(questions);
|
|
|
+ cloud.get.and.resolveTo(article);
|
|
|
|
|
|
await TestBed.configureTestingModule({
|
|
|
imports:[ReadingExerciseComponent],
|
|
|
@@ -62,7 +69,7 @@ describe('ReadingExerciseComponent', () => {
|
|
|
};
|
|
|
component.article.set(article);
|
|
|
component.questions.set(questions);
|
|
|
- component.answers = ['A', 'D'];
|
|
|
+ component.answers = ['A', 'D', 'D', 'D', 'D'];
|
|
|
});
|
|
|
|
|
|
it('waits for the answer log to save before showing completion and blocks duplicate clicks', async () => {
|
|
|
@@ -91,7 +98,7 @@ describe('ReadingExerciseComponent', () => {
|
|
|
|
|
|
await component.submit();
|
|
|
|
|
|
- expect(component.answers).toEqual(['A', 'D']);
|
|
|
+ expect(component.answers).toEqual(['A', 'D', 'D', 'D', 'D']);
|
|
|
expect(component.submitted()).toBeFalse();
|
|
|
expect(component.saveFailed()).toBeTrue();
|
|
|
expect(component.primaryButtonLabel).toBe('保存失败,重新保存');
|
|
|
@@ -104,7 +111,7 @@ describe('ReadingExerciseComponent', () => {
|
|
|
|
|
|
await component.primaryAction();
|
|
|
|
|
|
- expect(component.answers).toEqual(['A', 'D']);
|
|
|
+ expect(component.answers).toEqual(['A', 'D', 'D', 'D', 'D']);
|
|
|
expect(router.navigateByUrl).toHaveBeenCalledWith('/pages/his/his?date=2026-09-10', { replaceUrl:true });
|
|
|
expect(reading.saveLog).not.toHaveBeenCalled();
|
|
|
});
|
|
|
@@ -120,4 +127,70 @@ describe('ReadingExerciseComponent', () => {
|
|
|
expect(await component.canDeactivate()).toBeTrue();
|
|
|
expect(platform.confirm).not.toHaveBeenCalled();
|
|
|
});
|
|
|
+
|
|
|
+ it('blocks printing a partial set and prints only after five saved questions are available', async () => {
|
|
|
+ component.loading.set(false);
|
|
|
+ component.questions.set(questions.slice(0, 1));
|
|
|
+ await component.print();
|
|
|
+ expect(component.canPrint).toBeFalse();
|
|
|
+ expect(router.navigate).not.toHaveBeenCalled();
|
|
|
+
|
|
|
+ component.questions.set(questions);
|
|
|
+ await component.print();
|
|
|
+ expect(component.canPrint).toBeTrue();
|
|
|
+ expect(router.navigate).toHaveBeenCalledWith(['/words/reading/pdf', article.objectId], jasmine.any(Object));
|
|
|
+ });
|
|
|
+
|
|
|
+ it('fills an unsubmitted historical set before exposing it for printing', async () => {
|
|
|
+ component.params.sid = article.objectId;
|
|
|
+ reading.getQuestions.and.resolveTo(questions.slice(0, 1));
|
|
|
+ await component.load();
|
|
|
+ expect(reading.createQuestions).toHaveBeenCalled();
|
|
|
+ expect(reading.supplyMissingWords).not.toHaveBeenCalled();
|
|
|
+ expect(component.questions().length).toBe(5);
|
|
|
+ expect(component.canPrint).toBeTrue();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('preserves a submitted incomplete historical set and prevents another submission or print', async () => {
|
|
|
+ component.params.sid = article.objectId;
|
|
|
+ reading.latestLog.and.resolveTo(savedLog);
|
|
|
+ reading.getQuestions.and.resolveTo(questions.slice(0, 1));
|
|
|
+ await component.load();
|
|
|
+ expect(component.legacyIncomplete()).toBeTrue();
|
|
|
+ expect(component.submitted()).toBeTrue();
|
|
|
+ expect(component.canPrint).toBeFalse();
|
|
|
+ expect(reading.createQuestions).not.toHaveBeenCalled();
|
|
|
+ await component.print();
|
|
|
+ expect(router.navigate).not.toHaveBeenCalled();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('retries a failed new article against the saved article instead of generating another', async () => {
|
|
|
+ reading.createArticle.and.resolveTo(article);
|
|
|
+ reading.getQuestions.and.resolveTo([]);
|
|
|
+ reading.supplyMissingWords.and.resolveTo(article);
|
|
|
+ reading.createQuestions.and.rejectWith(new Error('题目保存失败'));
|
|
|
+ await component.load();
|
|
|
+ expect(component.params.sid).toBe(article.objectId);
|
|
|
+ expect(component.canPrint).toBeFalse();
|
|
|
+ expect(router.navigate).toHaveBeenCalledWith(['/words/reading'], jasmine.objectContaining({
|
|
|
+ queryParams: jasmine.objectContaining({ sid: article.objectId, renew: false }), replaceUrl: true,
|
|
|
+ }));
|
|
|
+
|
|
|
+ reading.createQuestions.and.resolveTo(questions);
|
|
|
+ await component.load();
|
|
|
+ expect(reading.createArticle).toHaveBeenCalledTimes(1);
|
|
|
+ expect(component.questions().length).toBe(5);
|
|
|
+ expect(component.canPrint).toBeTrue();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('offers a fresh article for a submitted incomplete legacy set even when the normal quota is full', async () => {
|
|
|
+ component.legacyIncomplete.set(true);
|
|
|
+ component.remainingArticles.set(0);
|
|
|
+ const reload = spyOn(component, 'load').and.resolveTo();
|
|
|
+ await component.createNewArticle();
|
|
|
+ expect(router.navigate).toHaveBeenCalledWith(['/words/reading'], jasmine.objectContaining({
|
|
|
+ queryParams: jasmine.objectContaining({ renew: true, sid: undefined }), replaceUrl: true,
|
|
|
+ }));
|
|
|
+ expect(reload).toHaveBeenCalled();
|
|
|
+ });
|
|
|
});
|