Skip to content
150 changes: 149 additions & 1 deletion frontend/cypress/e2e/campaign.cy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,154 @@
describe('Round Creation Validation', () => {
beforeEach(() => {
cy.setCookie('clastic_cookie', '<cookie-validation-string>')

cy.intercept('GET', '/v1/admin/user', {
status: 'success',
user: {
id: 1,
username: 'tester',
is_organizer: true,
is_maintainer: false,
last_active_date: '2025-08-01T09:30:00',
created_by: null
}
}).as('getUser')

cy.intercept('GET', '/v1/admin/campaign/123', {
fixture: 'roundValidationCampaign.json'
}).as('getCampaign')

cy.intercept('GET', '**/w/api.php*', {
query: {
globalallusers: [
{
name: 'AadarshM07',
id: 101
}
]
}
}).as('searchUser')

cy.visit('http://localhost:5173/#/campaign/123')
cy.wait('@getUser')
cy.wait('@getCampaign')
})

it('should show actionable validation errors when creating a round', () => {
cy.get('.add-round-button').click()

cy.get('.form-container input[placeholder="YYYY-MM-DD"]').first().type('2025-08-15')
cy.get('.form-container input[type="number"]').first().clear().type('1')
cy.get('[data-testid="userlist-search"] input').type('Ada')
cy.wait('@searchUser')
cy.get('[data-testid="userlist-search"]').find('li').first().click()

cy.contains('Add Round').click()
cy.contains('Please fill all the boxes (category is required for category import)').should('be.visible')

cy.get('[data-testid="cancel-round-button"]').click()
cy.get('.add-round-button').click()
cy.get('.form-container input[placeholder="YYYY-MM-DD"]').first().type('2025-08-15')
cy.get('.form-container input[type="number"]').first().clear().type('1')
cy.get('[data-testid="userlist-search"] input').type('Ada')
cy.wait('@searchUser')
cy.get('[data-testid="userlist-search"]').find('li').first().click()
cy.contains('File List URL').click()
cy.get('input[type="url"]').type('not-a-url')
cy.contains('Add Round').click()
cy.contains('Please fill all the boxes (provide a valid http/https CSV URL)').should('be.visible')

cy.get('[data-testid="cancel-round-button"]').click()
cy.get('.add-round-button').click()
cy.get('.form-container input[placeholder="YYYY-MM-DD"]').first().type('2025-08-15')
cy.get('.form-container input[type="number"]').first().clear().type('1')
cy.get('[data-testid="userlist-search"] input').type('Ada')
cy.wait('@searchUser')
cy.get('[data-testid="userlist-search"]').find('li').first().click()
cy.contains('File List').click()
cy.contains('Add Round').click()
cy.contains('Please fill all the boxes (add at least one filename for file list import)').should('be.visible')
})
})

describe('Round Edit Validation', () => {
beforeEach(() => {
cy.setCookie('clastic_cookie', '<cookie-validation-string>')

cy.intercept('GET', '/v1/admin/user', {
status: 'success',
user: {
id: 1,
username: 'tester',
is_organizer: true,
is_maintainer: false,
last_active_date: '2025-08-01T09:30:00',
created_by: null
}
}).as('getUser')

cy.intercept('GET', '/v1/admin/campaign/124', {
fixture: 'roundEditValidationCampaign.json'
}).as('getCampaign')

cy.intercept('GET', '/v1/admin/round/9001', {
fixture: 'roundEditDetails.json'
}).as('getRound')

cy.intercept('GET', '/v1/admin/round/9001/preview_results', {
data: {
counts: {
all_mimes: ['image/jpeg'],
percent_tasks_open: 0,
total_cancelled_tasks: 0,
total_disqualified_entries: 0,
total_open_tasks: 0,
total_round_entries: 1,
total_tasks: 1,
total_uploaders: 1
}
},
status: 'success'
}).as('previewRound')

cy.intercept('POST', '/v1/admin/round/9001/edit', {
status: 'success',
data: {}
}).as('editRound')

cy.visit('http://localhost:5173/#/campaign/124')
cy.wait('@getUser')
cy.wait('@getCampaign')
cy.wait('@getRound')
cy.wait('@previewRound')
})

it('should show actionable validation errors when editing a round', () => {
cy.contains('Edit round').click()
cy.get('.form-container input[type="text"]').first().clear()
cy.contains('Save').click()
cy.contains('Please fill all the boxes (round name is required)').should('be.visible')
cy.get('@editRound.all').should('have.length', 0)
})

it('should submit a valid round edit', () => {
cy.contains('Edit round').click()
cy.get('.form-container input[type="text"]').first().clear().type('Updated Round 1')
cy.contains('Save').click()

cy.wait('@editRound')
.its('request.body')
.then((body) => {
expect(body.name).to.eq('Updated Round 1')
expect(body.quorum).to.eq(1)
expect(body.new_jurors).to.deep.eq(['AadarshM07'])
})
})
})

describe('Campaign Details Page', () => {
beforeEach(() => {
cy.setCookie('clastic_cookie', '<cookie-validation-string>');
cy.setCookie('clastic_cookie', '<cookie-validation-string>')
cy.visit('http://localhost:5173/#/')
cy.get('div.coordinator-campaign-cards').find('.coordinator-campaign-card').first().click()
cy.url().should('match', /\/campaign\/\d+/)
Expand Down
41 changes: 41 additions & 0 deletions frontend/cypress/fixtures/roundEditDetails.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"data": {
"id": 9001,
"name": "Round 1",
"directions": "Review entries carefully.",
"canonical_url_name": "round-1",
"vote_method": "yesno",
"open_date": "2025-08-01T10:00:00",
"deadline_date": "2025-08-08T18:00:00",
"close_date": null,
"jurors": [
{
"id": 11,
"username": "AadarshM07",
"is_active": true,
"stats": {
"total_tasks": 1,
"total_open_tasks": 0,
"percent_tasks_open": 0,
"total_cancelled_tasks": 0
}
}
],
"status": "active",
"quorum": 1,
"show_stats": false,
"config": {
"dq_by_resolution": false,
"min_resolution": 2000000,
"dq_by_uploader": true,
"dq_by_upload_date": false,
"dq_coords": false,
"dq_maintainers": false,
"dq_organizers": false,
"show_filename": false,
"show_link": false,
"show_resolution": false
}
},
"status": "success"
}
62 changes: 62 additions & 0 deletions frontend/cypress/fixtures/roundEditValidationCampaign.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"data": {
"id": 124,
"name": "Edit Validation Campaign",
"url_name": "edit-validation-campaign",
"open_date": "2025-08-01T10:00:00",
"close_date": "2025-08-10T18:00:00",
"create_date": "2025-08-01T09:30:00",
"status": "active",
"is_archived": false,
"isArchived": false,
"coordinators": [
{
"id": 1,
"username": "tester",
"is_organizer": true,
"is_maintainer": false,
"last_active_date": "2025-08-01T09:30:00",
"created_by": null
}
],
"rounds": [
{
"id": 9001,
"name": "Round 1",
"directions": "Review entries carefully.",
"canonical_url_name": "round-1",
"vote_method": "yesno",
"open_date": "2025-08-01T10:00:00",
"deadline_date": "2025-08-08T18:00:00",
"close_date": null,
"jurors": [
{
"id": 11,
"username": "AadarshM07",
"is_active": true
}
],
"status": "active",
"config": {
"dq_by_resolution": false,
"min_resolution": 2000000,
"dq_by_uploader": true,
"dq_by_upload_date": false,
"dq_coords": false,
"dq_maintainers": false,
"dq_organizers": false,
"show_filename": false,
"show_link": false,
"show_resolution": false
}
}
],
"active_round": {
"id": 9001,
"name": "Round 1",
"vote_method": "yesno",
"status": "active"
}
},
"status": "success"
}
26 changes: 26 additions & 0 deletions frontend/cypress/fixtures/roundValidationCampaign.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"data": {
"id": 123,
"name": "Validation Campaign",
"url_name": "validation-campaign",
"open_date": "2025-08-01T10:00:00",
"close_date": "2025-08-10T18:00:00",
"create_date": "2025-08-01T09:30:00",
"status": "active",
"is_archived": false,
"isArchived": false,
"coordinators": [
{
"id": 1,
"username": "tester",
"is_organizer": true,
"is_maintainer": false,
"last_active_date": "2025-08-01T09:30:00",
"created_by": null
}
],
"rounds": [],
"active_round": null
},
"status": "success"
}
2 changes: 1 addition & 1 deletion frontend/src/components/Campaign/ViewCampaign.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
:rounds="campaignRounds"
v-if="showAddRoundForm"
v-model:showAddRoundForm="showAddRoundForm"
@reloadCampaignState="reloadState"
@reload-campaign-state="reloadState"
/>
</div>
</div>
Expand Down
26 changes: 25 additions & 1 deletion frontend/src/components/Round/RoundEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import { CdxButton, CdxField, CdxTextInput, CdxRadio, CdxTextArea } from '@wikim
import Check from 'vue-material-design-icons/Check.vue'
import Close from 'vue-material-design-icons/Close.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
import { hasEnoughJurors, parsePositiveQuorum } from '@/utils'
import alertService from '@/services/alertService'
import adminService from '@/services/adminService'
import dialogService from '@/services/dialogService'
Expand Down Expand Up @@ -149,18 +150,41 @@ const cancelRound = () => {
emit('update:isRoundEditing', false)
}

const showValidationError = (detail) => {
alertService.error({
message: `${$t('montage-required-fill-inputs')} (${detail})`
})
}

const saveRound = () => {
const quorum = parsePositiveQuorum(formData.value.quorum)

if (!formData.value.deadline_date) {
alertService.error({
message: $t('montage-required-voting-deadline')
})
return
}

if (!formData.value.name) {
showValidationError('round name is required')
return
}

if (quorum === null) {
showValidationError('quorum must be a number greater than or equal to 1')
return
}

if (!hasEnoughJurors(formData.value.jurors, quorum)) {
showValidationError(`add at least ${quorum} juror(s) to match quorum`)
return
}

const round = {
id: props.round.id,
name: formData.value.name,
quorum: formData.value.quorum,
quorum,
directions: formData.value.directions,
show_stats: formData.value.show_stats,
deadline_date: formData.value.deadline_date + 'T00:00:00',
Expand Down
Loading