From eae7801ec17e3c835a06f7424d2a3b1be146ec56 Mon Sep 17 00:00:00 2001 From: Eva Lee Date: Tue, 15 Oct 2024 21:51:01 +1100 Subject: [PATCH 1/2] implement the issue #13175 --- ...tion-response-panel.component.spec.ts.snap | 3 + .../question-response-panel.component.html | 3 +- .../question-response-panel.component.ts | 3 + ...session-result-page.component.spec.ts.snap | 122 +++++++++++++++++- .../session-result-page.component.html | 6 + .../session-result-page.component.ts | 2 + .../session-result-page.module.ts | 2 + 7 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/web/app/components/question-response-panel/__snapshots__/question-response-panel.component.spec.ts.snap b/src/web/app/components/question-response-panel/__snapshots__/question-response-panel.component.spec.ts.snap index 269667eb6d3..5587730ac41 100644 --- a/src/web/app/components/question-response-panel/__snapshots__/question-response-panel.component.spec.ts.snap +++ b/src/web/app/components/question-response-panel/__snapshots__/question-response-panel.component.spec.ts.snap @@ -4,6 +4,7 @@ exports[`QuestionResponsePanelComponent should snap with feedback session with q Other responses (to you): Responses are not visible to you. -
+ +
Your own responses (to others):
diff --git a/src/web/app/components/question-response-panel/question-response-panel.component.ts b/src/web/app/components/question-response-panel/question-response-panel.component.ts index e44f8a56f55..c69b04bf183 100644 --- a/src/web/app/components/question-response-panel/question-response-panel.component.ts +++ b/src/web/app/components/question-response-panel/question-response-panel.component.ts @@ -64,6 +64,9 @@ export class QuestionResponsePanelComponent { @Input() previewAsPerson: string = ''; + @Input() + hideSelfResponses: boolean = false; + canUserSeeResponses(question: FeedbackQuestionModel): boolean { const showResponsesTo: FeedbackVisibilityType[] = question.feedbackQuestion.showResponsesTo; if (this.intent === Intent.STUDENT_RESULT) { diff --git a/src/web/app/pages-session/session-result-page/__snapshots__/session-result-page.component.spec.ts.snap b/src/web/app/pages-session/session-result-page/__snapshots__/session-result-page.component.spec.ts.snap index 1fb615bcc24..49bfaaff642 100644 --- a/src/web/app/pages-session/session-result-page/__snapshots__/session-result-page.component.spec.ts.snap +++ b/src/web/app/pages-session/session-result-page/__snapshots__/session-result-page.component.spec.ts.snap @@ -17,6 +17,7 @@ exports[`SessionResultPageComponent should snap when previewing results 1`] = ` formattedSessionClosingTime="" formattedSessionOpeningTime="" hasFeedbackSessionResultsLoadingFailed="false" + hideSelfResponses="false" instructorService={[Function InstructorService]} intent={[Function String]} isCourseLoading="false" @@ -128,6 +129,20 @@ exports[`SessionResultPageComponent should snap when previewing results 1`] = `
+
+ +
+
+ +
+
+ +
+
+ +
- +
+ + +
+
+ +
+
+ +
+
+ +
Previewing Session Results as
+
+ + +
+
If you wish to view the feedback results of the entire course, click here. @@ -82,6 +87,7 @@

Previewing Session Results as

diff --git a/src/web/app/pages-session/session-result-page/session-result-page.component.ts b/src/web/app/pages-session/session-result-page/session-result-page.component.ts index 5769cbda252..2effdef1456 100644 --- a/src/web/app/pages-session/session-result-page/session-result-page.component.ts +++ b/src/web/app/pages-session/session-result-page/session-result-page.component.ts @@ -106,6 +106,8 @@ export class SessionResultPageComponent implements OnInit { feedbackSessionId: string | undefined = ''; studentId: string | undefined = ''; + hideSelfResponses: boolean = false; + private backendUrl: string = environment.backendUrl; constructor(private feedbackQuestionsService: FeedbackQuestionsService, diff --git a/src/web/app/pages-session/session-result-page/session-result-page.module.ts b/src/web/app/pages-session/session-result-page/session-result-page.module.ts index 424ff2267d1..fd6764a82b3 100644 --- a/src/web/app/pages-session/session-result-page/session-result-page.module.ts +++ b/src/web/app/pages-session/session-result-page/session-result-page.module.ts @@ -1,5 +1,6 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { RouterModule, Routes } from '@angular/router'; import { SessionResultPageComponent } from './session-result-page.component'; @@ -25,6 +26,7 @@ const routes: Routes = [ @NgModule({ imports: [ CommonModule, + FormsModule, QuestionTextWithInfoModule, StudentViewResponsesModule, SingleStatisticsModule, From 8fd99434497b3a7f59e2e11f3b0ab468ec51b8ef Mon Sep 17 00:00:00 2001 From: Zijian Yang <176981575+Zijian-Yang@users.noreply.github.com> Date: Sat, 19 Oct 2024 15:03:35 +1100 Subject: [PATCH 2/2] test: integrate jest-preset-angular for improved Angular testing compatibility and update related configurations --- jest-setup.ts | 27 ++++-- jest.config.js | 33 +++++-- package-lock.json | 57 +++++++++--- package.json | 3 +- .../hide-my-responses.component.spec.ts | 90 +++++++++++++++++++ tsconfig.spec.json | 12 ++- 6 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 src/web/app/components/question-response-panel/hide-my-responses.component.spec.ts diff --git a/jest-setup.ts b/jest-setup.ts index d6345cd0773..efce5cbeecd 100644 --- a/jest-setup.ts +++ b/jest-setup.ts @@ -1,9 +1,22 @@ -require('core-js/es/reflect'); -require('core-js/proposals/reflect-metadata'); +import 'zone.js'; +import 'zone.js/testing'; +import { TestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); + require('@angular/localize/init'); -(window as any).IntersectionObserver = jest.fn(() => ({ - observe: jest.fn(), - unobserve: jest.fn(), - disconnect: jest.fn(), -})); +Object.defineProperty(window, 'IntersectionObserver', { + writable: true, + value: jest.fn(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + })), +}); + +Object.defineProperty(global, 'IntersectionObserver', { + writable: true, + value: window.IntersectionObserver, +}); diff --git a/jest.config.js b/jest.config.js index c6e671a938b..2737c42e12a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,29 @@ module.exports = { + preset: 'jest-preset-angular', + setupFilesAfterEnv: ['/jest-setup.ts'], + testEnvironment: 'jsdom', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.html$', + useESM: true, + }, + }, + moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'], + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + '^app/(.*)$': '/src/app/$1', + '^assets/(.*)$': '/src/assets/$1', + '^environments/(.*)$': '/src/environments/$1', + }, + transformIgnorePatterns: ['node_modules/(?!@angular|rxjs|@ng-bootstrap|zone.js)'], + transform: { + '^.+\\.(ts|js|mjs|html|svg)$': ['jest-preset-angular', { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.html$', + useESM: true, + }], + }, collectCoverageFrom: [ 'src/web/app/**/*.ts', '!src/web/app/**/*.module.ts', @@ -7,12 +32,4 @@ module.exports = { ], coverageDirectory: './coverage', coverageReporters: ['lcov', 'text-summary'], - setupFiles: [ - './jest-setup.ts', - ], - moduleNameMapper: { - d3: '/node_modules/d3/dist/d3.min.js', - 'lodash-es': 'lodash' - }, - globalSetup: 'jest-preset-angular/global-setup', }; diff --git a/package-lock.json b/package-lock.json index 6267ea605d9..3b3cce2b174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "@angular/compiler-cli": "^16.2.12", "@types/d3": "^7.4.3", "@types/file-saver": "^2.0.7", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.13", "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", @@ -62,6 +62,7 @@ "eslint-plugin-jsdoc": "^48.0.4", "eslint-plugin-rxjs": "^5.0.3", "jest": "^29.7.0", + "jest-preset-angular": "^14.2.4", "lintspaces-cli": "^0.7.1", "npm-run-all": "^4.1.5", "postcss-html": "^1.6.0", @@ -118,6 +119,34 @@ "jest": ">=29" } }, + "node_modules/@angular-builders/jest/node_modules/jest-preset-angular": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-13.1.1.tgz", + "integrity": "sha512-X8i7icKt9U5uhj7YKqdEZm7ZZPvNFRxfBnU+9SALdIkHYJhwtlJ5/MUk9wo4f3lX2smOkIl9LPJUu1APO+11Jg==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.13.8", + "jest-environment-jsdom": "^29.0.0", + "jest-util": "^29.0.0", + "pretty-format": "^29.0.0", + "ts-jest": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": ">=0.13.8" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=13.0.0 <17.0.0", + "@angular/compiler-cli": ">=13.0.0 <17.0.0", + "@angular/core": ">=13.0.0 <17.0.0", + "@angular/platform-browser-dynamic": ">=13.0.0 <17.0.0", + "jest": "^29.0.0", + "typescript": ">=4.4" + } + }, "node_modules/@angular-devkit/architect": { "version": "0.1602.12", "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.12.tgz", @@ -6018,9 +6047,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -14356,13 +14385,13 @@ } }, "node_modules/jest-preset-angular": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-13.1.1.tgz", - "integrity": "sha512-X8i7icKt9U5uhj7YKqdEZm7ZZPvNFRxfBnU+9SALdIkHYJhwtlJ5/MUk9wo4f3lX2smOkIl9LPJUu1APO+11Jg==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.2.4.tgz", + "integrity": "sha512-xyhkaiBdn3keBgxxkcbqZu/my3ADU9NcDrz6DaMuGRaxz/bf6ZC1qxZ1eQuz5V1WuA3/rD64VA3Kke8P6E9qNg==", "dev": true, "dependencies": { "bs-logger": "^0.2.6", - "esbuild-wasm": ">=0.13.8", + "esbuild-wasm": ">=0.15.13", "jest-environment-jsdom": "^29.0.0", "jest-util": "^29.0.0", "pretty-format": "^29.0.0", @@ -14372,15 +14401,15 @@ "node": "^14.15.0 || >=16.10.0" }, "optionalDependencies": { - "esbuild": ">=0.13.8" + "esbuild": ">=0.15.13" }, "peerDependencies": { - "@angular-devkit/build-angular": ">=13.0.0 <17.0.0", - "@angular/compiler-cli": ">=13.0.0 <17.0.0", - "@angular/core": ">=13.0.0 <17.0.0", - "@angular/platform-browser-dynamic": ">=13.0.0 <17.0.0", + "@angular-devkit/build-angular": ">=15.0.0 <19.0.0", + "@angular/compiler-cli": ">=15.0.0 <19.0.0", + "@angular/core": ">=15.0.0 <19.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <19.0.0", "jest": "^29.0.0", - "typescript": ">=4.4" + "typescript": ">=4.8" } }, "node_modules/jest-regex-util": { diff --git a/package.json b/package.json index cbb8c5b3689..f7adeb994ee 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@angular/compiler-cli": "^16.2.12", "@types/d3": "^7.4.3", "@types/file-saver": "^2.0.7", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.13", "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", @@ -74,6 +74,7 @@ "eslint-plugin-jsdoc": "^48.0.4", "eslint-plugin-rxjs": "^5.0.3", "jest": "^29.7.0", + "jest-preset-angular": "^14.2.4", "lintspaces-cli": "^0.7.1", "npm-run-all": "^4.1.5", "postcss-html": "^1.6.0", diff --git a/src/web/app/components/question-response-panel/hide-my-responses.component.spec.ts b/src/web/app/components/question-response-panel/hide-my-responses.component.spec.ts new file mode 100644 index 00000000000..003c53c7aec --- /dev/null +++ b/src/web/app/components/question-response-panel/hide-my-responses.component.spec.ts @@ -0,0 +1,90 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { QuestionResponsePanelComponent } from './question-response-panel.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { FeedbackSessionsService } from '../../../services/feedback-sessions.service'; +import { StatusMessageService } from '../../../services/status-message.service'; + +describe('HideMyResponses Feature', () => { + let component: QuestionResponsePanelComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ QuestionResponsePanelComponent ], + imports: [ + HttpClientTestingModule, + RouterTestingModule, + ], + providers: [ + FeedbackSessionsService, + StatusMessageService, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(QuestionResponsePanelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be able to toggle hide/show responses', fakeAsync(() => { + expect(component.hideSelfResponses).toBeFalsy(); + + component.hideSelfResponses = true; + tick(); + fixture.detectChanges(); + expect(component.hideSelfResponses).toBeTruthy(); + + component.hideSelfResponses = false; + tick(); + fixture.detectChanges(); + expect(component.hideSelfResponses).toBeFalsy(); + })); + + it('should initialize with default values', () => { + expect(component.session).toBeDefined(); + expect(component.session.courseId).toBe(''); + expect(component.questions).toEqual([]); + expect(component.regKey).toBe(''); + expect(component.previewAsPerson).toBe(''); + }); + + it('should have a defined FeedbackSessionsService', () => { + const feedbackSessionsService = TestBed.inject(FeedbackSessionsService); + expect(feedbackSessionsService).toBeDefined(); + }); + + it('should have a defined StatusMessageService', () => { + const statusMessageService = TestBed.inject(StatusMessageService); + expect(statusMessageService).toBeDefined(); + }); + + it('should render without errors', () => { + expect(fixture.nativeElement).toBeTruthy(); + }); + + it('should have RESPONSE_HIDDEN_QUESTIONS array with "CONTRIB" initially', () => { + expect(component.RESPONSE_HIDDEN_QUESTIONS).toEqual(['CONTRIB']); + }); + + it('should have "STUDENT_RESULT" as initial intent', () => { + expect(component.intent).toBe('STUDENT_RESULT'); + }); + + it('should update hideSelfResponses when toggled multiple times', fakeAsync(() => { + component.hideSelfResponses = true; + tick(); + fixture.detectChanges(); + expect(component.hideSelfResponses).toBeTruthy(); + + component.hideSelfResponses = false; + tick(); + fixture.detectChanges(); + expect(component.hideSelfResponses).toBeFalsy(); + + component.hideSelfResponses = true; + tick(); + fixture.detectChanges(); + expect(component.hideSelfResponses).toBeTruthy(); + })); +}); diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 110a7b71029..1878d5cbd56 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -2,17 +2,15 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "module": "commonjs", - "resolveJsonModule": true, - "esModuleInterop": true, - "emitDecoratorMetadata": true, "types": [ "jest", "node" - ] + ], + "esModuleInterop": true, + "emitDecoratorMetadata": true }, "include": [ - "**/*.spec.ts", - "**/*.d.ts" + "src/**/*.spec.ts", + "src/**/*.d.ts" ] }