Files
twenty/packages/twenty-website/src/partner-application/validate-partner-application-step.test.ts
T
Rashad Karanouh 6b99bcea7f feat(partners): require Twenty experience on apply, drop Cal success (#23223)
## Summary
- Add a dedicated **Experience** step to the partner apply wizard
(milestones, ≥200-char narrative, proof URL) before commercials
- Stop collecting `applicationNotes`; rename Expertise chrome away from
“experience”
- Replace the post-submit Cal.com embed with a review-and-reach-out
thank-you so unqualified inbound no longer books intros automatically

**Companion PR (app):** #23224 — Partner schema, submit persistence,
triage views, Tally CSV mapper (`twenty-partners` v1.4.0). Land the app
PR with or before this one.

## Test plan
- [ ] Open apply modal: wizard order is identity → profile → expertise →
experience → commercials
- [ ] Experience step blocks continue without ≥1 milestone, narrative
≥200 chars, and a valid https URL
- [ ] Submit creates/updates Partner with the three experience fields
(with #23224 deployed)
- [ ] Success screen has no Cal embed / book-later CTA
- [ ] `npx jest --config=jest.config.mjs partner-application` passes
locally (71 tests)

---------

Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com>
2026-07-24 09:40:22 +05:00

53 lines
2.1 KiB
TypeScript

import { INITIAL_PARTNER_APPLICATION_STATE } from './partner-application-state';
import { validatePartnerApplicationStep } from './validate-partner-application-step';
const validExperienceNotes =
'Built a custom Twenty app for a property-management client, modeled leases and ' +
'tenants as data models, automated renewal workflows, and shipped a front component ' +
'for the broker dashboard with role-based views.';
describe('validatePartnerApplicationStep', () => {
it('requires experience milestones, narrative, and proof URL on Experience', () => {
const errors = validatePartnerApplicationStep({
...INITIAL_PARTNER_APPLICATION_STATE,
stepIndex: 3,
});
expect(errors.twentyExperience).toBe('required');
expect(errors.twentyExperienceNotes).toBe('required');
expect(errors.twentyExperienceProofLink).toBe('required');
});
it('rejects a narrative under 200 characters on Experience', () => {
const errors = validatePartnerApplicationStep({
...INITIAL_PARTNER_APPLICATION_STATE,
stepIndex: 3,
twentyExperience: ['WORKFLOWS'],
twentyExperienceNotes: 'Too short for a real implementation narrative.',
twentyExperienceProofLink: 'https://www.loom.com/share/example',
});
expect(errors.twentyExperienceNotes).toBe('too_short');
});
it('rejects an invalid proof URL on Experience', () => {
const errors = validatePartnerApplicationStep({
...INITIAL_PARTNER_APPLICATION_STATE,
stepIndex: 3,
twentyExperience: ['CUSTOM_APPS'],
twentyExperienceNotes: validExperienceNotes,
twentyExperienceProofLink: 'not-a-url',
});
expect(errors.twentyExperienceProofLink).toBe('invalid_url');
});
it('accepts a complete Experience step', () => {
const errors = validatePartnerApplicationStep({
...INITIAL_PARTNER_APPLICATION_STATE,
stepIndex: 3,
twentyExperience: ['CUSTOM_APPS', 'DATA_MODELS'],
twentyExperienceNotes: validExperienceNotes,
twentyExperienceProofLink: 'https://www.loom.com/share/example',
});
expect(errors).toEqual({});
});
});