A call now has its address — description, what the volunteer gets, the
commitment added up, level, closing date, project, technologies — and
the application form right there. The landing's call card leads to it
instead of scrolling to the general form, and shows the level in the
visitor's language rather than the API's `mid` or `any`.
The form is the landing's, with a `callSlug` that hides the kind
selector and posts to the new /api/calls/{slug}/apply handler: the API
refuses a `kind` on an application, which is always an offer of time.
Both handlers share one check, core/submission.ts, now tested on its
own.
A call that is filled or closed keeps its page — a link to it may
still be out there — but shows why there is no form, and where to
write instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
1.7 KiB
TypeScript
68 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { checkSubmission } from './submission';
|
|
|
|
const valid = {
|
|
name: 'Ada',
|
|
email: 'ada@example.org',
|
|
message: 'Me gustaría echar una mano con el frontend.',
|
|
consent: true,
|
|
};
|
|
|
|
describe('checkSubmission', () => {
|
|
it('passes a complete contact through, trimmed', () => {
|
|
const { payload } = checkSubmission({
|
|
...valid,
|
|
name: ' Ada ',
|
|
hours_per_week: '4',
|
|
});
|
|
|
|
expect(payload).toMatchObject({
|
|
name: 'Ada',
|
|
kind: 'contribute',
|
|
hours_per_week: 4,
|
|
});
|
|
});
|
|
|
|
it('names every missing field at once', () => {
|
|
const { errors } = checkSubmission({ message: 'corto' });
|
|
|
|
expect(Object.keys(errors ?? {}).sort()).toEqual([
|
|
'consent',
|
|
'email',
|
|
'message',
|
|
'name',
|
|
]);
|
|
});
|
|
|
|
it('asks an organisation asking for help to name itself', () => {
|
|
expect(
|
|
checkSubmission({ ...valid, kind: 'request' }).errors,
|
|
).toHaveProperty('organisation');
|
|
expect(
|
|
checkSubmission({ ...valid, kind: 'request', organisation: 'Asociación' })
|
|
.payload,
|
|
).toMatchObject({
|
|
kind: 'request',
|
|
});
|
|
});
|
|
|
|
// The API refuses a `kind` on an application to a call: it is always an
|
|
// offer of time.
|
|
it('sends no kind with an application to a call, whatever the body says', () => {
|
|
const { payload } = checkSubmission(
|
|
{ ...valid, kind: 'request' },
|
|
{ forCall: true },
|
|
);
|
|
|
|
expect(payload).toBeDefined();
|
|
expect(payload).not.toHaveProperty('kind');
|
|
});
|
|
|
|
it('keeps the honeypot, so the API can file a bot as spam', () => {
|
|
expect(
|
|
checkSubmission({ ...valid, website: 'http://spam' }).payload,
|
|
).toHaveProperty('website');
|
|
});
|
|
});
|