diff --git a/package-lock.json b/package-lock.json
index c6349618..a8248606 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,16 @@
{
"name": "ag-website-vue",
- "version": "1.6.1",
+ "version": "1.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ag-website-vue",
- "version": "1.6.1",
+ "version": "1.7.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/minimatch": "^3.0.5",
- "ag-client-typescript": "2.5.0",
+ "ag-client-typescript": "2.6.0",
"chart.js": "^3.9.1",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-zoom": "^1.2.1",
@@ -4736,9 +4736,9 @@
}
},
"node_modules/ag-client-typescript": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/ag-client-typescript/-/ag-client-typescript-2.5.0.tgz",
- "integrity": "sha512-ro4rw3gyreSDXJOMAIJQbg738eFh6rqlTlaa+oqD04hCO+G1CJ+LvneYBucdwRYmnaGcS/h/t9dXagKMJ+lssw==",
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/ag-client-typescript/-/ag-client-typescript-2.6.0.tgz",
+ "integrity": "sha512-6WLbPhHISELCVC9hcTJzkKZnVbKJ8wM4ICif2s4O5TxSz5jc5/7GCnyzfEhrLn1NyCGCedppswK1uiLUkxm6jA==",
"dependencies": {
"axios": "^0.21.1"
}
diff --git a/package.json b/package.json
index 678bcaa9..566a18b0 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/minimatch": "^3.0.5",
- "ag-client-typescript": "2.5.0",
+ "ag-client-typescript": "2.6.0",
"chart.js": "^3.9.1",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-zoom": "^1.2.1",
diff --git a/src/components/project_view/handgrading/group_summary_panel.vue b/src/components/project_view/handgrading/group_summary_panel.vue
index ded35cdd..e770b257 100644
--- a/src/components/project_view/handgrading/group_summary_panel.vue
+++ b/src/components/project_view/handgrading/group_summary_panel.vue
@@ -4,7 +4,7 @@
'graded': status === HandgradingStatus.graded,
'ungraded': status === HandgradingStatus.ungraded,
'in-progress': status === HandgradingStatus.in_progress,
- 'no-submission': status === HandgradingStatus.no_submission,
+ 'no-handgradeable-submission': status === HandgradingStatus.no_submission,
}"
v-on="$listeners">
@@ -78,7 +78,7 @@ export default class GroupSummaryPanel extends Vue {
color: $ocean-blue;
}
-.no-submission {
+.no-handgradeable-submission {
color: $stormy-gray-dark;
}
diff --git a/src/components/project_view/handgrading/handgrading_container.vue b/src/components/project_view/handgrading/handgrading_container.vue
index 77d36aed..42f11a9e 100644
--- a/src/components/project_view/handgrading/handgrading_container.vue
+++ b/src/components/project_view/handgrading/handgrading_container.vue
@@ -68,12 +68,14 @@
-
+ :value="HandgradingStatus.no_handgradeable_submission">
+
@@ -90,7 +92,7 @@
:class="{
'active': d_currently_grading !== null
&& d_currently_grading.group === group_summary.pk,
- 'disabled': group_summary.num_submissions === 0
+ 'disabled': !group_summary.has_handgradeable_submission
}">
@@ -234,13 +236,13 @@ export default class HandgradingContainer extends Vue implements ag_cli.Handgrad
get total_num_to_grade() {
return this.staff_filtered_groups.filter(
- group => get_handgrading_status(group) !== HandgradingStatus.no_submission
+ group => get_handgrading_status(group) !== HandgradingStatus.no_handgradeable_submission
).length;
}
@handle_global_errors_async
async select_for_grading(group: ag_cli.GroupWithHandgradingResultSummary) {
- if (group.num_submissions !== 0) {
+ if (group.has_handgradeable_submission) {
await toggle(this, 'd_loading_result', async () => {
this.d_currently_grading = await ag_cli.HandgradingResult.get_or_create(group.pk);
});
@@ -250,7 +252,7 @@ export default class HandgradingContainer extends Vue implements ag_cli.Handgrad
get previous() {
let index = this.index_of_currently_grading - 1;
while (index >= 0) {
- if (this.staff_filtered_groups[index].num_submissions !== 0) {
+ if (this.staff_filtered_groups[index].has_handgradeable_submission) {
return this.staff_filtered_groups[index];
}
index -= 1;
@@ -261,7 +263,7 @@ export default class HandgradingContainer extends Vue implements ag_cli.Handgrad
get next() {
let index = this.index_of_currently_grading + 1;
while (index < this.staff_filtered_groups.length) {
- if (this.staff_filtered_groups[index].num_submissions !== 0) {
+ if (this.staff_filtered_groups[index].has_handgradeable_submission) {
return this.staff_filtered_groups[index];
}
index += 1;
diff --git a/src/components/project_view/handgrading/handgrading_status.ts b/src/components/project_view/handgrading/handgrading_status.ts
index 620bedb2..c34a1ed5 100644
--- a/src/components/project_view/handgrading/handgrading_status.ts
+++ b/src/components/project_view/handgrading/handgrading_status.ts
@@ -1,15 +1,15 @@
import { GroupWithHandgradingResultSummary } from 'ag-client-typescript';
export enum HandgradingStatus {
- no_submission = "No Submission",
+ no_handgradeable_submission = "No Submission",
ungraded = "Ungraded",
in_progress = "In Progress",
graded = "Graded",
}
export function get_handgrading_status(group_summary: GroupWithHandgradingResultSummary) {
- if (group_summary.num_submissions === 0) {
- return HandgradingStatus.no_submission;
+ if (!group_summary.has_handgradeable_submission) {
+ return HandgradingStatus.no_handgradeable_submission;
}
let result = group_summary.handgrading_result;
diff --git a/tests/data_utils.ts b/tests/data_utils.ts
index c03440d1..e6dc2dfe 100644
--- a/tests/data_utils.ts
+++ b/tests/data_utils.ts
@@ -697,15 +697,17 @@ export function make_group_summary(
project_pk: number,
num_members: number = 1,
group_args: Partial = {},
+ has_handgradeable_submission: boolean = false,
handgrading_result: {
finished_grading: boolean;
total_points: number;
total_points_possible: number;
- } | null = null
+ } | null = null,
): GroupWithHandgradingResultSummary {
let group = make_group(project_pk, num_members, group_args);
return {
handgrading_result: handgrading_result,
+ has_handgradeable_submission: has_handgradeable_submission,
...group
};
}
diff --git a/tests/test_components/test_project_view/test_handgrading/test_group_summary_panel.ts b/tests/test_components/test_project_view/test_handgrading/test_group_summary_panel.ts
index 634cefdd..e94ad05a 100644
--- a/tests/test_components/test_project_view/test_handgrading/test_group_summary_panel.ts
+++ b/tests/test_components/test_project_view/test_handgrading/test_group_summary_panel.ts
@@ -29,7 +29,7 @@ test('Group member names displayed', () => {
test('Score displayed when status is graded', () => {
let summary = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
{finished_grading: true, total_points: 4, total_points_possible: 5}
);
let wrapper = managed_mount(GroupSummaryPanel, {
@@ -43,7 +43,7 @@ test('Score displayed when status is graded', () => {
test('Non-graded statuses show status text', async () => {
let in_progress_summary = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
{finished_grading: false, total_points: 3, total_points_possible: 5}
);
let wrapper = managed_mount(GroupSummaryPanel, {
@@ -54,14 +54,20 @@ test('Non-graded statuses show status text', async () => {
expect(wrapper.find('.status').text()).toEqual('In Progress');
let ungraded_summary = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
);
await set_props(wrapper, {group_summary: ungraded_summary});
expect(wrapper.find('.status').text()).toEqual('Ungraded');
let no_submission_summary = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 0},
+ project.pk, 1, {num_submissions: 0}, false,
);
await set_props(wrapper, {group_summary: no_submission_summary});
expect(wrapper.find('.status').text()).toEqual('No Submission');
+
+ let no_handgradeable_submission_summary = data_ut.make_group_summary(
+ project.pk, 1, {num_submissions: 1}, false,
+ );
+ await set_props(wrapper, {group_summary: no_handgradeable_submission_summary});
+ expect(wrapper.find('.status').text()).toEqual('No Submission');
});
diff --git a/tests/test_components/test_project_view/test_handgrading/test_handgrading_container.ts b/tests/test_components/test_project_view/test_handgrading/test_handgrading_container.ts
index 65970c37..e033d0b4 100644
--- a/tests/test_components/test_project_view/test_handgrading/test_handgrading_container.ts
+++ b/tests/test_components/test_project_view/test_handgrading/test_handgrading_container.ts
@@ -10,13 +10,14 @@ import { HandgradingStatus } from '@/components/project_view/handgrading/handgra
import * as data_ut from '@/tests/data_utils';
import { managed_mount } from '@/tests/setup';
-import { checkbox_is_checked, compress_whitespace, find_by_name, wait_until } from '@/tests/utils';
+import { checkbox_is_checked, compress_whitespace, expect_has_class, expect_not_has_class, find_by_name, wait_until } from '@/tests/utils';
let course: ag_cli.Course;
let project: ag_cli.Project;
let rubric: ag_cli.HandgradingRubric;
let no_submissions_group: ag_cli.GroupWithHandgradingResultSummary;
+let no_handgradeable_submissions_group: ag_cli.GroupWithHandgradingResultSummary;
let ungraded_group: ag_cli.GroupWithHandgradingResultSummary;
let in_progress_group: ag_cli.GroupWithHandgradingResultSummary;
let graded_group: ag_cli.GroupWithHandgradingResultSummary;
@@ -43,11 +44,14 @@ beforeEach(() => {
).rejects(new ag_cli.HttpError(500, 'Mock me please'));
no_submissions_group = data_ut.make_group_summary(
- project.pk, 1, {member_names: ['none@me.com']});
+ project.pk, 1, {member_names: ['none@me.com'], num_submissions: 0}, false);
+ no_handgradeable_submissions_group = data_ut.make_group_summary(
+ project.pk, 1, {member_names: ['none@me.com'], num_submissions: 1}, false);
ungraded_group = data_ut.make_group_summary(
- project.pk, 1, {member_names: ['not_yet@me.com'], num_submissions: 1});
+ project.pk, 1, {member_names: ['not_yet@me.com'], num_submissions: 1}, true);
in_progress_group = data_ut.make_group_summary(
project.pk, 1, {member_names: ['progress@me.com'], num_submissions: 1},
+ true,
{
finished_grading: false,
total_points: 4,
@@ -56,6 +60,7 @@ beforeEach(() => {
);
graded_group = data_ut.make_group_summary(
project.pk, 1, {member_names: ['graded@me.com'], num_submissions: 1},
+ true,
{
finished_grading: true,
total_points: 5,
@@ -70,6 +75,7 @@ beforeEach(() => {
staff_group = data_ut.make_group_summary(
project.pk, 1,
{member_names: staff.map(user => user.username), num_submissions: 1},
+ true,
{
finished_grading: true,
total_points: 6,
@@ -82,6 +88,7 @@ beforeEach(() => {
admin_group = data_ut.make_group_summary(
project.pk, 1,
{member_names: [admin.username], num_submissions: 1},
+ true,
{
finished_grading: true,
total_points: 5,
@@ -99,6 +106,7 @@ describe('Filter group summaries tests', () => {
beforeEach(async () => {
set_summaries([
no_submissions_group,
+ no_handgradeable_submissions_group,
ungraded_group,
in_progress_group,
graded_group,
@@ -112,21 +120,22 @@ describe('Filter group summaries tests', () => {
test('Include/exclude staff', async () => {
expect(checkbox_is_checked(wrapper.find('#include-staff'))).toBe(false);
expect(wrapper.vm.d_include_staff).toBe(false);
- expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(4);
+ expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(5);
expect(summary_pks(wrapper).includes(staff_group.pk)).toBe(false);
wrapper.find('#include-staff').setChecked();
await wrapper.vm.$nextTick();
expect(wrapper.vm.d_include_staff).toBe(true);
- expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(6);
+ expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(7);
expect(summary_pks(wrapper).includes(staff_group.pk)).toBe(true);
});
test('Filter by status', async () => {
expect(checkbox_is_checked(wrapper.find('#all'))).toBe(true);
- expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(4);
+ expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(5);
expect(summary_pks(wrapper)).toEqual([
no_submissions_group.pk,
+ no_handgradeable_submissions_group.pk,
ungraded_group.pk,
in_progress_group.pk,
graded_group.pk,
@@ -147,10 +156,12 @@ describe('Filter group summaries tests', () => {
expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(1);
expect(summary_pks(wrapper)).toEqual([ungraded_group.pk]);
- wrapper.find('#no-submission').setChecked();
+ wrapper.find('#no-handgradeable-submission').setChecked();
await wrapper.vm.$nextTick();
- expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(1);
- expect(summary_pks(wrapper)).toEqual([no_submissions_group.pk]);
+ expect(wrapper.findAllComponents({name: 'GroupSummaryPanel'}).length).toBe(2);
+ expect(
+ summary_pks(wrapper)
+ ).toEqual([no_submissions_group.pk, no_handgradeable_submissions_group.pk]);
});
test('Filter by username', async () => {
@@ -170,21 +181,21 @@ describe('Filter group summaries tests', () => {
expect(wrapper.vm.d_include_staff).toBe(false);
expect(
compress_whitespace(wrapper.findComponent({ref: 'progress_text'}).text())
- ).toEqual('1/3 (4 total)');
+ ).toEqual('1/3 (5 total)');
wrapper.vm.d_include_staff = true;
await wrapper.vm.$nextTick();
expect(
compress_whitespace(wrapper.findComponent({ref: 'progress_text'}).text())
- ).toEqual('3/5 (6 total)');
+ ).toEqual('3/5 (7 total)');
// Filtering by status does NOT change the "total graded" display
wrapper.vm.d_status_filter = HandgradingStatus.graded;
await wrapper.vm.$nextTick();
expect(
compress_whitespace(wrapper.findComponent({ref: 'progress_text'}).text())
- ).toEqual('3/5 (6 total)');
+ ).toEqual('3/5 (7 total)');
// Entering search text does NOT change the "total graded" display
@@ -192,7 +203,7 @@ describe('Filter group summaries tests', () => {
await wrapper.vm.$nextTick();
expect(
compress_whitespace(wrapper.findComponent({ref: 'progress_text'}).text())
- ).toEqual('3/5 (6 total)');
+ ).toEqual('3/5 (7 total)');
});
});
@@ -257,7 +268,12 @@ describe('Select group tests', () => {
let wrapper: Wrapper;
beforeEach(async () => {
- set_summaries([no_submissions_group, ungraded_group, graded_group]);
+ set_summaries([
+ no_submissions_group,
+ no_handgradeable_submissions_group,
+ ungraded_group,
+ graded_group
+ ]);
wrapper = await make_wrapper();
});
@@ -266,8 +282,12 @@ describe('Select group tests', () => {
get_or_create_stub.withArgs(ungraded_group.pk).resolves(result);
expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
- await wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(1).trigger('click');
+ let to_click = wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(2);
+ await to_click.trigger('click');
await expect_result_is_selected(wrapper, result);
+
+ expect_not_has_class(to_click, 'disabled');
+ expect_has_class(to_click, 'active');
});
test('Select graded group for grading', async () => {
@@ -275,17 +295,45 @@ describe('Select group tests', () => {
get_or_create_stub.withArgs(graded_group.pk).resolves(result);
expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
- await wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(2).trigger('click');
+ let to_click = wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(3);
+ await to_click.trigger('click');
await expect_result_is_selected(wrapper, result);
+
+ expect_not_has_class(to_click, 'disabled');
+ expect_has_class(to_click, 'active');
});
test('Group selected for grading has no submissions', async () => {
+ let result = data_ut.make_handgrading_result(rubric, no_submissions_group.pk, 42);
+ get_or_create_stub.withArgs(no_submissions_group.pk).resolves(result);
+
expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
- wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(0).trigger('click');
- await wrapper.vm.$nextTick();
+ let to_click = wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(0);
+ await to_click.trigger('click');
expect(wrapper.vm.d_currently_grading).toBe(null);
expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
+ expect(get_or_create_stub.called).toBe(false);
+
+ expect_has_class(to_click, 'disabled');
+ expect_not_has_class(to_click, 'active');
+ });
+
+ test('Group selected for grading has no handgradeable submissions', async () => {
+ let result = data_ut.make_handgrading_result(
+ rubric, no_handgradeable_submissions_group.pk, 42);
+ get_or_create_stub.withArgs(no_handgradeable_submissions_group.pk).resolves(result);
+
+ expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
+ let to_click = wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(1);
+ await to_click.trigger('click');
+
+ expect(wrapper.vm.d_currently_grading).toBe(null);
+ expect(wrapper.findComponent({name: 'Handgrading'}).exists()).toBe(false);
+ expect(get_or_create_stub.called).toBe(false);
+
+ expect_has_class(to_click, 'disabled');
+ expect_not_has_class(to_click, 'active');
});
});
@@ -318,7 +366,12 @@ describe('Select next/prev for grading', () => {
});
test('Select next and prev skips no_submission', async () => {
- set_summaries([ungraded_group, no_submissions_group, graded_group]);
+ set_summaries([
+ ungraded_group,
+ no_submissions_group,
+ no_handgradeable_submissions_group,
+ graded_group
+ ]);
let wrapper = await make_wrapper();
await wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(0).trigger('click');
@@ -360,6 +413,17 @@ describe('Select next/prev for grading', () => {
expect(find_by_name(wrapper, 'Handgrading').vm.is_last).toBe(false);
});
+ test('Current group is first with handgradeable submission', async () => {
+ set_summaries([no_handgradeable_submissions_group, ungraded_group, graded_group]);
+
+ let wrapper = await make_wrapper();
+ await wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(1).trigger('click');
+ await expect_result_is_selected(wrapper, ungraded_result);
+
+ expect(find_by_name(wrapper, 'Handgrading').vm.is_first).toBe(true);
+ expect(find_by_name(wrapper, 'Handgrading').vm.is_last).toBe(false);
+ });
+
test('Current group is last with submission', async () => {
set_summaries([ungraded_group, graded_group, no_submissions_group]);
@@ -371,6 +435,17 @@ describe('Select next/prev for grading', () => {
expect(find_by_name(wrapper, 'Handgrading').vm.is_last).toBe(true);
});
+ test('Current group is last with handgradeable submission', async () => {
+ set_summaries([ungraded_group, graded_group, no_handgradeable_submissions_group]);
+
+ let wrapper = await make_wrapper();
+ await wrapper.findAllComponents({name: 'GroupSummaryPanel'}).at(1).trigger('click');
+ await expect_result_is_selected(wrapper, graded_result);
+
+ expect(find_by_name(wrapper, 'Handgrading').vm.is_first).toBe(false);
+ expect(find_by_name(wrapper, 'Handgrading').vm.is_last).toBe(true);
+ });
+
test('Current group is only group', async () => {
set_summaries([ungraded_group]);
diff --git a/tests/test_components/test_project_view/test_handgrading/test_handgrading_status.ts b/tests/test_components/test_project_view/test_handgrading/test_handgrading_status.ts
index a8240e13..4039551c 100644
--- a/tests/test_components/test_project_view/test_handgrading/test_handgrading_status.ts
+++ b/tests/test_components/test_project_view/test_handgrading/test_handgrading_status.ts
@@ -12,24 +12,25 @@ beforeEach(() => {
test('get_handgrading_status', () => {
let graded = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
{finished_grading: true, total_points: 4, total_points_possible: 5}
);
expect(get_handgrading_status(graded)).toEqual(HandgradingStatus.graded);
let in_progress = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
{finished_grading: false, total_points: 3, total_points_possible: 5}
);
expect(get_handgrading_status(in_progress)).toEqual(HandgradingStatus.in_progress);
let ungraded = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 1},
+ project.pk, 1, {num_submissions: 1}, true,
);
expect(get_handgrading_status(ungraded)).toEqual(HandgradingStatus.ungraded);
- let no_submission = data_ut.make_group_summary(
- project.pk, 1, {num_submissions: 0},
+ let no_handgradeable_submission = data_ut.make_group_summary(
+ project.pk, 1, {num_submissions: 1}, false
);
- expect(get_handgrading_status(no_submission)).toEqual(HandgradingStatus.no_submission);
+ expect(get_handgrading_status(no_handgradeable_submission))
+ .toEqual(HandgradingStatus.no_handgradeable_submission);
});
diff --git a/tests/utils.ts b/tests/utils.ts
index 74e62fd3..fd67dda1 100644
--- a/tests/utils.ts
+++ b/tests/utils.ts
@@ -219,3 +219,17 @@ type SetDataInputPartial = {
export function api_error_count(wrapper: Wrapper, selector: RefSelector) {
return ( wrapper.findComponent(selector).vm).d_api_errors.length;
}
+
+export function expect_has_class(wrapper: Wrapper, class_: string) {
+ let class_list = Array.from(wrapper.element.classList);
+ if (!class_list.includes(class_)) {
+ fail(`CSS class "${class_}" not found in [${class_list.join(', ')}]`);
+ }
+}
+
+export function expect_not_has_class(wrapper: Wrapper, class_: string) {
+ let class_list = Array.from(wrapper.element.classList);
+ if (class_list.includes(class_)) {
+ fail(`CSS class "${class_}" unexpectedly found in [${class_list.join(', ')}]`);
+ }
+}