|
1 | | -import { defineComponent } from 'vue'; |
| 1 | +import { defineComponent, watch } from 'vue'; |
2 | 2 | import { fireEvent, render, screen } from '@testing-library/vue'; |
3 | 3 | import { axe } from 'vitest-axe'; |
4 | 4 | import { StepResolveContext, useStepFormFlow } from '.'; |
@@ -1433,3 +1433,141 @@ describe('warnings', () => { |
1433 | 1433 | ); |
1434 | 1434 | }); |
1435 | 1435 | }); |
| 1436 | + |
| 1437 | +describe('single-step forms', () => { |
| 1438 | + test('should correctly identify isLastStep as true for single-step form', async () => { |
| 1439 | + await render({ |
| 1440 | + components: { |
| 1441 | + SteppedFormFlow, |
| 1442 | + FormFlowSegment, |
| 1443 | + TextField, |
| 1444 | + }, |
| 1445 | + template: ` |
| 1446 | + <SteppedFormFlow> |
| 1447 | + <FormFlowSegment name="only-step"> |
| 1448 | + <span>Only Step</span> |
| 1449 | + <TextField label="Name" name="name" /> |
| 1450 | + </FormFlowSegment> |
| 1451 | + </SteppedFormFlow> |
| 1452 | + `, |
| 1453 | + }); |
| 1454 | + |
| 1455 | + await flush(); |
| 1456 | + |
| 1457 | + // With only one step, the next button should say "Submit" not "Next" |
| 1458 | + expect(screen.getByTestId('next-button')).toHaveTextContent('Submit'); |
| 1459 | + }); |
| 1460 | + |
| 1461 | + test('should trigger onDone when submitting single-step form', async () => { |
| 1462 | + const onDone = vi.fn(); |
| 1463 | + |
| 1464 | + await render({ |
| 1465 | + setup() { |
| 1466 | + return { onDone }; |
| 1467 | + }, |
| 1468 | + components: { |
| 1469 | + SteppedFormFlow, |
| 1470 | + FormFlowSegment, |
| 1471 | + TextField, |
| 1472 | + }, |
| 1473 | + template: ` |
| 1474 | + <SteppedFormFlow @done="onDone"> |
| 1475 | + <FormFlowSegment name="only-step"> |
| 1476 | + <TextField label="Name" name="name" /> |
| 1477 | + </FormFlowSegment> |
| 1478 | + </SteppedFormFlow> |
| 1479 | + `, |
| 1480 | + }); |
| 1481 | + |
| 1482 | + await flush(); |
| 1483 | + |
| 1484 | + // Fill in the field |
| 1485 | + await fireEvent.update(screen.getByLabelText('Name'), 'John Doe'); |
| 1486 | + await flush(); |
| 1487 | + |
| 1488 | + // Click next/submit button |
| 1489 | + await fireEvent.click(screen.getByTestId('next-button')); |
| 1490 | + await flush(); |
| 1491 | + |
| 1492 | + // Should have triggered onDone, not moved to another step |
| 1493 | + expect(onDone).toHaveBeenCalledWith({ |
| 1494 | + name: 'John Doe', |
| 1495 | + }); |
| 1496 | + }); |
| 1497 | + |
| 1498 | + test('should not change button text while segments are registering', async () => { |
| 1499 | + // This test verifies the segmentsStable flag works correctly |
| 1500 | + // by checking that isLastStep doesn't prematurely return true during registration |
| 1501 | + let buttonTexts: string[] = []; |
| 1502 | + |
| 1503 | + await render({ |
| 1504 | + setup() { |
| 1505 | + const { isLastStep } = useStepFormFlow(); |
| 1506 | + |
| 1507 | + // Track button text as it changes |
| 1508 | + watch( |
| 1509 | + () => isLastStep.value, |
| 1510 | + val => { |
| 1511 | + buttonTexts.push(val ? 'Submit' : 'Next'); |
| 1512 | + }, |
| 1513 | + { immediate: true }, |
| 1514 | + ); |
| 1515 | + |
| 1516 | + return { isLastStep }; |
| 1517 | + }, |
| 1518 | + components: { |
| 1519 | + FormFlowSegment, |
| 1520 | + TextField, |
| 1521 | + }, |
| 1522 | + template: ` |
| 1523 | + <div> |
| 1524 | + <FormFlowSegment name="step1"> |
| 1525 | + <TextField label="Name" name="name" /> |
| 1526 | + </FormFlowSegment> |
| 1527 | + <FormFlowSegment name="step2"> |
| 1528 | + <TextField label="Email" name="email" /> |
| 1529 | + </FormFlowSegment> |
| 1530 | + <FormFlowSegment name="step3"> |
| 1531 | + <TextField label="Phone" name="phone" /> |
| 1532 | + </FormFlowSegment> |
| 1533 | + <button>{{ isLastStep ? 'Submit' : 'Next' }}</button> |
| 1534 | + </div> |
| 1535 | + `, |
| 1536 | + }); |
| 1537 | + |
| 1538 | + await flush(); |
| 1539 | + |
| 1540 | + // The button should start as "Next" and not flicker to "Submit" during registration |
| 1541 | + // First value should be "Next" (not "Submit") |
| 1542 | + expect(buttonTexts[0]).toBe('Next'); |
| 1543 | + |
| 1544 | + // After all segments register and stabilize, should still be "Next" (we're on step 1 of 3) |
| 1545 | + expect(screen.getByRole('button')).toHaveTextContent('Next'); |
| 1546 | + }); |
| 1547 | + |
| 1548 | + test('single-step form isLastStep should become true after segments stabilize', async () => { |
| 1549 | + await render({ |
| 1550 | + components: { |
| 1551 | + SteppedFormFlow, |
| 1552 | + FormFlowSegment, |
| 1553 | + TextField, |
| 1554 | + }, |
| 1555 | + template: ` |
| 1556 | + <SteppedFormFlow> |
| 1557 | + <FormFlowSegment name="only-step"> |
| 1558 | + <TextField label="Name" name="name" /> |
| 1559 | + </FormFlowSegment> |
| 1560 | + </SteppedFormFlow> |
| 1561 | + `, |
| 1562 | + }); |
| 1563 | + |
| 1564 | + await flush(); |
| 1565 | + |
| 1566 | + // Wait an extra tick for the microtask (segmentsStable) to resolve |
| 1567 | + await new Promise(resolve => Promise.resolve().then(resolve)); |
| 1568 | + await flush(); |
| 1569 | + |
| 1570 | + // After segments stabilize, single-step form should show "Submit" |
| 1571 | + expect(screen.getByTestId('next-button')).toHaveTextContent('Submit'); |
| 1572 | + }); |
| 1573 | +}); |
0 commit comments