From b4e3c36cf6381a8713e403c2c9cff6aebdb18bec Mon Sep 17 00:00:00 2001 From: Carine Dengler Date: Thu, 15 Feb 2024 18:21:04 +0100 Subject: [PATCH 1/2] test: add tests for handling minimum/maximum/correct number of votes and ballot display --- web/frontend/tests/ballot.spec.ts | 76 ++++++++++++++++++++++++++++++- web/frontend/tests/mocks/api.ts | 16 +++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/web/frontend/tests/ballot.spec.ts b/web/frontend/tests/ballot.spec.ts index 36e14d83e..f463f3b4b 100644 --- a/web/frontend/tests/ballot.spec.ts +++ b/web/frontend/tests/ballot.spec.ts @@ -2,8 +2,15 @@ import { expect, test } from '@playwright/test'; import { default as i18n } from 'i18next'; import { assertHasFooter, assertHasNavBar, initI18n, logIn, setUp } from './shared'; import { FORMID } from './mocks/shared'; -import { SCIPER_ADMIN, SCIPER_OTHER_USER, SCIPER_USER, mockPersonalInfo } from './mocks/api'; +import { + SCIPER_ADMIN, + SCIPER_OTHER_USER, + SCIPER_USER, + mockFormsVote, + mockPersonalInfo, +} from './mocks/api'; import { mockFormsFormID } from './mocks/evoting'; +import Form from './json/evoting/forms/open.json'; initI18n(); @@ -47,3 +54,70 @@ test('Assert ballot form is correctly handled for anonymous users, non-voter use await expect(page.getByText(i18n.t('voteExplanation'))).toBeVisible(); }); }); + +test('Assert ballot is displayed properly', async ({ page }) => { + const content = await page.getByTestId('content'); + // TODO integrate localisation + i18n.changeLanguage('en'); // force 'en' for this test + await expect(content.locator('xpath=./div/div[3]/h3')).toContainText(Form.Configuration.Title.En); + const scaffold = Form.Configuration.Scaffold.at(0); + await expect(content.locator('xpath=./div/div[3]/div/div/h3')).toContainText(scaffold.Title.En); + const select = scaffold.Selects.at(0); + await expect( + content.locator('xpath=./div/div[3]/div/div/div/div/div/div[1]/div/h3') + ).toContainText(select.Title.En); + await expect( + page.getByText(i18n.t('selectBetween', { minSelect: select.MinN, maxSelect: select.MaxN })) + ).toBeVisible(); + for (const choice of select.Choices.map((x) => JSON.parse(x))) { + await expect(page.getByRole('checkbox', { name: choice.en })).toBeVisible(); + } + i18n.changeLanguage(); // unset language for the other tests +}); + +test('Assert minimum/maximum number of choices are handled correctly', async ({ page }) => { + const castVoteButton = await page.getByRole('button', { name: i18n.t('castVote') }); + const select = Form.Configuration.Scaffold.at(0).Selects.at(0); + await test.step( + `Assert minimum number of choices (${select.MinN}) are handled correctly`, + async () => { + await castVoteButton.click(); + await expect( + page.getByText( + i18n.t('minSelectError', { min: select.MinN, singularPlural: i18n.t('singularAnswer') }) + ) + ).toBeVisible(); + } + ); + await test.step( + `Assert maximum number of choices (${select.MaxN}) are handled correctly`, + async () => { + for (const choice of select.Choices.map((x) => JSON.parse(x))) { + await page.getByRole('checkbox', { name: choice.en }).setChecked(true); + } + await castVoteButton.click(); + await expect(page.getByText(i18n.t('maxSelectError', { max: select.MaxN }))).toBeVisible(); + } + ); +}); + +test('Assert that correct number of choices are accepted', async ({ page, baseURL }) => { + await mockFormsVote(page); + page.waitForRequest(async (request) => { + const body = await request.postDataJSON(); + return ( + request.url() === `${baseURL}/api/evoting/forms/${FORMID}/vote` && + request.method() === 'POST' && + body.UserID === null && + body.Ballot.length === 1 && + body.Ballot.at(0).K.length === 32 && + body.Ballot.at(0).C.length === 32 + ); + }); + await page + .getByRole('checkbox', { + name: JSON.parse(Form.Configuration.Scaffold.at(0).Selects.at(0).Choices.at(0)).en, + }) + .setChecked(true); + await page.getByRole('button', { name: i18n.t('castVote') }).click(); +}); diff --git a/web/frontend/tests/mocks/api.ts b/web/frontend/tests/mocks/api.ts index 0ce10b83a..70aae8ef0 100644 --- a/web/frontend/tests/mocks/api.ts +++ b/web/frontend/tests/mocks/api.ts @@ -38,6 +38,22 @@ export async function mockForms(page: page) { }); } +export async function mockFormsVote(page: page) { + await page.route(`/api/evoting/forms/${FORMID}/vote`, async (route) => { + if (route.request().method() === 'POST') { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: { + Status: 0, + Token: + 'eyJTdGF0dXMiOjAsIlRyYW5zYWN0aW9uSUQiOiJQQWluaEVjNVNzM2JiVWkxbldNWU55dWdPVkFpdVZ3YklZcGpKTFJ1SUdnPSIsIkxhc3RCbG9ja0lkeCI6NSwiVGltZSI6MTcwODAxNDgyMSwiSGFzaCI6ImtVT3g3Ykw0eC9IYXdwanppTityTVFIL3Fmb1pnRHBFUFc3S2tzRWl1TTA9IiwiU2lnbmF0dXJlIjoiZXlKT1lXMWxJam9pUWt4VExVTlZVbFpGTFVKT01qVTJJaXdpUkdGMFlTSTZJbU00UjFwUVYyWjZTRlpqT1dGV1dWaGhTbEUwWVhKT1UyRTRVbXB2VEVOSGIyZFBlbWRpVDFKSlYxVnVSbkE0ZFdaemQyMWlVVXA2ZWpScWJHNVFRa3QzUzJwWmVEaDJkVmgzZUhCWE5FeE1hVFZWUkRsUlBUMGlmUT09In0=', + }, + }); + } + }); +} + // /api/config export async function mockProxy(page: page) { From a0abffda477361d92285d4d43df85483b406910a Mon Sep 17 00:00:00 2001 From: Carine Dengler Date: Thu, 15 Feb 2024 18:27:27 +0100 Subject: [PATCH 2/2] fix: fix test w/ non-unique identifier --- web/frontend/tests/ballot.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/frontend/tests/ballot.spec.ts b/web/frontend/tests/ballot.spec.ts index f463f3b4b..256678de5 100644 --- a/web/frontend/tests/ballot.spec.ts +++ b/web/frontend/tests/ballot.spec.ts @@ -50,7 +50,7 @@ test('Assert ballot form is correctly handled for anonymous users, non-voter use await page.goto(`/ballot/show/${FORMID}`, { waitUntil: 'networkidle' }); await expect(page).toHaveURL(`/ballot/show/${FORMID}`); await expect(castVoteButton).toBeVisible(); - await expect(page.getByText(i18n.t('vote'))).toBeVisible(); + await expect(page.getByText(i18n.t('vote'), { exact: true })).toBeVisible(); await expect(page.getByText(i18n.t('voteExplanation'))).toBeVisible(); }); });