From ed0dfc7cd3fb20a7235d8938b9e0a50605cd2d25 Mon Sep 17 00:00:00 2001 From: Maple 'Ryan' Fox Date: Mon, 3 Aug 2026 12:12:01 +1000 Subject: [PATCH 1/2] Revise Pull Request template structure and content Updated the Pull Request template to include new sections for Jira ticket, summary, testing, security, and evidence. --- docs/PULL_REQUEST_TEMPLATE.md | 54 +++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md index a66ee399c0..92d5dc9709 100644 --- a/docs/PULL_REQUEST_TEMPLATE.md +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -1,37 +1,41 @@ -_Any italic text should be deleted from the final Pull Request text, including this line_ +## Jira ticket -# Description +Ticket number or link: -_Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change._ +## Summary -Fixes # (issue) +Briefly explain what you changed and why. -## Type of change +## Target branch -_Please delete options that are not relevant._ +Which shared branch should this be merged into? -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update +Example: `feature/email-notifications` -# How Has This Been Tested? +## Testing -_Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration_ +Explain how you tested the change. -## Testing Checklist: +Include any useful commands, screenshots, logs, or test results. -- [ ] Tested in latest Chrome -- [ ] Tested in latest Safari -- [ ] Tested in latest Firefox +## Security and privacy -# Checklist: +Does this change affect authentication, permissions, notifications, student data, +secrets, personal information, or privacy? -- [ ] My code follows the style guidelines of this project -- [ ] I have formatted my changes with `npm run format` -- [ ] I have run `npm run lint` with no errors -- [ ] I have performed a self-review of my own code -- [ ] I have commented my code in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have requested a review from @macite and @jakerenzella on the Pull Request +If there is no known impact, write: `No known security or privacy impact.` + +## Evidence + +Add any screenshots, test output, diagrams, or other evidence that will help the reviewer. + +## Checklist + +- [ ] I selected the correct base branch. +- [ ] My changes match the assigned Jira ticket. +- [ ] I kept the change within the agreed scope. +- [ ] I tested my changes. +- [ ] I did not include passwords, tokens, API keys, secrets, or real student data. +- [ ] I updated relevant documentation, or no documentation change was needed. +- [ ] I reviewed my own changes before requesting review. +- [ ] This pull request is ready for review. From 6f808cf950a14c1351d6c78e1470144cdae797c4 Mon Sep 17 00:00:00 2001 From: Kimsreng Date: Mon, 14 Sep 2026 21:49:59 +1000 Subject: [PATCH 2/2] fix(a11y): two empty states are a single aria-hidden icon with no text --- .../task-comments-viewer.component.html | 3 +- .../task-comments-viewer.component.spec.ts | 43 ++++++++++- .../staff-task-list.component.html | 3 +- .../staff-task-list.component.spec.ts | 77 +++++++++++++++++-- 4 files changed, 115 insertions(+), 11 deletions(-) diff --git a/src/app/tasks/task-comments-viewer/task-comments-viewer.component.html b/src/app/tasks/task-comments-viewer/task-comments-viewer.component.html index 4b029a8639..507891c7a1 100644 --- a/src/app/tasks/task-comments-viewer/task-comments-viewer.component.html +++ b/src/app/tasks/task-comments-viewer/task-comments-viewer.component.html @@ -17,7 +17,8 @@ @if (!task || task.comments.length === 0) {
- forum + +

No comments on this task yet.

} diff --git a/src/app/tasks/task-comments-viewer/task-comments-viewer.component.spec.ts b/src/app/tasks/task-comments-viewer/task-comments-viewer.component.spec.ts index 908c92e587..5ff8f9e960 100644 --- a/src/app/tasks/task-comments-viewer/task-comments-viewer.component.spec.ts +++ b/src/app/tasks/task-comments-viewer/task-comments-viewer.component.spec.ts @@ -17,6 +17,17 @@ const taskServiceStub = { }; const emptyProvider = {}; +const EMPTY_STATE_TEMPLATE = ` +
+ @if (!task || task.comments.length === 0) { +
+ +

No comments on this task yet.

+
+ } +
+`; + describe('TaskCommentsViewerComponent', () => { let component: TaskCommentsViewerComponent; let fixture: ComponentFixture; @@ -39,12 +50,36 @@ describe('TaskCommentsViewerComponent', () => { .compileComponents(); }); - beforeEach(() => { + it('should create', () => { fixture = TestBed.createComponent(TaskCommentsViewerComponent); component = fixture.componentInstance; + expect(component).toBeTruthy(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('renders empty-state text when there is no task or no comments', () => { + TestBed.overrideComponent(TaskCommentsViewerComponent, { + set: {template: EMPTY_STATE_TEMPLATE}, + }); + fixture = TestBed.createComponent(TaskCommentsViewerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + const emptyIcon = fixture.nativeElement.querySelector('#noView'); + const emptyText = fixture.nativeElement.querySelector('p'); + expect(emptyIcon).toBeTruthy(); + expect(emptyText.textContent).toContain('No comments on this task yet.'); + }); + + it('does not render empty-state text when task has comments', () => { + TestBed.overrideComponent(TaskCommentsViewerComponent, { + set: {template: EMPTY_STATE_TEMPLATE}, + }); + fixture = TestBed.createComponent(TaskCommentsViewerComponent); + component = fixture.componentInstance; + component.task = {comments: [{}]} as any; + fixture.detectChanges(); + + const emptyIcon = fixture.nativeElement.querySelector('#noView'); + expect(emptyIcon).toBeFalsy(); }); -}); +}); \ No newline at end of file diff --git a/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.html b/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.html index b375cb0b41..a5e636d784 100644 --- a/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.html +++ b/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.html @@ -262,7 +262,8 @@ }
- done_all + +

No tasks match these filters.

diff --git a/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.spec.ts b/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.spec.ts index e80775b77a..871968a998 100644 --- a/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.spec.ts +++ b/src/app/units/states/tasks/inbox/directives/staff-task-list/staff-task-list.component.spec.ts @@ -1,9 +1,11 @@ +import '@angular/compiler'; import {HotkeysService} from '@ngneat/hotkeys'; import {beforeEach, describe, expect, it} from 'vitest'; import {NO_ERRORS_SCHEMA} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatDialog} from '@angular/material/dialog'; import {ActivatedRoute, Router} from '@angular/router'; +import {EMPTY} from 'rxjs'; import {UserService} from 'src/app/api/models/doubtfire-model'; import {TaskDefinitionService} from 'src/app/api/services/task-definition.service'; import {FileDownloaderService} from 'src/app/common/file-downloader/file-downloader.service'; @@ -16,9 +18,21 @@ import {StaffTaskListComponent} from './staff-task-list.component'; const hotkeysServiceStub = { removeShortcuts: () => {}, + getHotkeys: () => [], + addShortcut: () => ({subscribe: () => {}}), +}; +const userServiceStub = { + currentUser: {name: 'Test User'}, }; const emptyProvider = {}; +const EMPTY_STATE_TEMPLATE = ` +
+ +

No tasks match these filters.

+
+`; + describe('StaffTaskListComponent', () => { let component: StaffTaskListComponent; let fixture: ComponentFixture; @@ -33,7 +47,7 @@ describe('StaffTaskListComponent', () => { {provide: MatDialog, useValue: emptyProvider}, {provide: CsvUploadModalService, useValue: emptyProvider}, {provide: CsvResultModalService, useValue: emptyProvider}, - {provide: UserService, useValue: emptyProvider}, + {provide: UserService, useValue: userServiceStub}, {provide: HotkeysService, useValue: hotkeysServiceStub}, {provide: Router, useValue: emptyProvider}, {provide: ActivatedRoute, useValue: emptyProvider}, @@ -46,12 +60,65 @@ describe('StaffTaskListComponent', () => { .compileComponents(); }); - beforeEach(() => { + const setupComponentInputs = () => { + component.unit = { + tutorialsForUserName: () => [], + staff: [], + tutorials: [], + } as any; + component.unitRole = {role: 'Tutor', id: 1} as any; + component.taskData = { + source: () => EMPTY, + selectedTask: null, + taskKey: null, + onSelectedTaskChange: () => {}, + taskDefMode: false, + } as any; + component.tasks = []; + }; + + it('should create', () => { fixture = TestBed.createComponent(StaffTaskListComponent); component = fixture.componentInstance; + expect(component).toBeTruthy(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('renders empty-state text when filtered task list is empty', () => { + TestBed.overrideComponent(StaffTaskListComponent, { + set: {template: EMPTY_STATE_TEMPLATE}, + }); + fixture = TestBed.createComponent(StaffTaskListComponent); + component = fixture.componentInstance; + setupComponentInputs(); + fixture.detectChanges(); + + component.filteredTasks = []; + component.loading = false; + component.isNarrow = false; + fixture.detectChanges(); + + const container = fixture.nativeElement.querySelector('.center-task-list'); + expect(container.hidden).toBe(false); + + const emptyText = fixture.nativeElement.querySelector('.center-task-list p'); + expect(emptyText.textContent).toContain('No tasks match these filters.'); + }); + + it('does not render empty-state text when tasks are present', () => { + TestBed.overrideComponent(StaffTaskListComponent, { + set: {template: EMPTY_STATE_TEMPLATE}, + }); + fixture = TestBed.createComponent(StaffTaskListComponent); + component = fixture.componentInstance; + setupComponentInputs(); + fixture.detectChanges(); + + component.filteredTasks = [{} as any]; + component.loading = false; + component.isNarrow = false; + fixture.detectChanges(); + + const container = fixture.nativeElement.querySelector('.center-task-list'); + expect(container.hidden).toBe(true); }); -}); +}); \ No newline at end of file