@12-apps/ui 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -9
- package/src/button/index.ts +1 -0
- package/src/social-login-button/index.ts +1 -0
- package/src/{social-login-button.tsx → social-login-button/social-login-button.tsx} +5 -5
- package/src/user-avatar/index.ts +1 -0
- package/src/{user-avatar.tsx → user-avatar/user-avatar.tsx} +4 -4
- package/eslint.config.js +0 -89
- package/src/__tests__/data-grid.test.tsx +0 -125
- package/src/__tests__/package-wiring.test.ts +0 -41
- package/src/__tests__/setup.ts +0 -1
- package/src/__tests__/social-login-button.test.tsx +0 -186
- package/src/__tests__/user-avatar.test.tsx +0 -195
- package/src/__tests__/viewport.test.ts +0 -50
- package/src/components/command/__tests__/command.test.tsx +0 -112
- package/src/components/data-display/Alert/Alert.stories.tsx +0 -559
- package/src/components/data-display/Alert/Alert.test.stories.tsx +0 -707
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +0 -362
- package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +0 -763
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +0 -230
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +0 -270
- package/src/components/data-display/Avatar/Avatar.stories.tsx +0 -721
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +0 -822
- package/src/components/data-display/Badge/Badge.stories.tsx +0 -1134
- package/src/components/data-display/Badge/Badge.test.stories.tsx +0 -1194
- package/src/components/data-display/Banner/Banner.stories.tsx +0 -528
- package/src/components/data-display/Banner/Banner.test.stories.tsx +0 -511
- package/src/components/data-display/Carousel/Carousel.stories.tsx +0 -607
- package/src/components/data-display/Carousel/Carousel.test.stories.tsx +0 -701
- package/src/components/data-display/Chart/Chart.stories.tsx +0 -568
- package/src/components/data-display/Chart/Chart.test.stories.tsx +0 -174
- package/src/components/data-display/Chart/SpecChart.stories.tsx +0 -150
- package/src/components/data-display/Chart/SpecChart.test.stories.tsx +0 -56
- package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +0 -60
- package/src/components/data-display/Chart/__tests__/chart-format.test.ts +0 -43
- package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +0 -119
- package/src/components/data-display/Chip/Chip.stories.tsx +0 -252
- package/src/components/data-display/Chip/Chip.test.stories.tsx +0 -266
- package/src/components/data-display/DataGrid/DataGrid.stories.tsx +0 -515
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +0 -653
- package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +0 -271
- package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +0 -134
- package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +0 -83
- package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +0 -115
- package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +0 -53
- package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +0 -114
- package/src/components/data-display/DataViews/base-card.stories.tsx +0 -136
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +0 -254
- package/src/components/data-display/EmptyState/EmptyState.stories.tsx +0 -282
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +0 -441
- package/src/components/data-display/ErrorState/ErrorState.stories.tsx +0 -110
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +0 -133
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +0 -650
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +0 -929
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +0 -411
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +0 -387
- package/src/components/data-display/Lightbox/Lightbox.stories.tsx +0 -315
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +0 -598
- package/src/components/data-display/LoadingState/LoadingState.stories.tsx +0 -124
- package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +0 -99
- package/src/components/data-display/MapPreview/MapPreview.stories.tsx +0 -458
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +0 -394
- package/src/components/data-display/Popover/Popover.stories.tsx +0 -295
- package/src/components/data-display/Popover/Popover.test.stories.tsx +0 -1162
- package/src/components/data-display/Progress/Progress.stories.tsx +0 -481
- package/src/components/data-display/Progress/Progress.test.stories.tsx +0 -2070
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +0 -160
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +0 -190
- package/src/components/data-display/Sheet/Sheet.stories.tsx +0 -883
- package/src/components/data-display/Sheet/Sheet.test.stories.tsx +0 -1105
- package/src/components/data-display/StatCard/StatCard.stories.tsx +0 -167
- package/src/components/data-display/StatCard/StatCard.test.stories.tsx +0 -240
- package/src/components/data-display/Stepper/Stepper.stories.tsx +0 -540
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +0 -600
- package/src/components/data-display/Table/Table.stories.tsx +0 -803
- package/src/components/data-display/Table/Table.test.stories.tsx +0 -369
- package/src/components/data-display/Timeline/Timeline.stories.tsx +0 -442
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +0 -930
- package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +0 -475
- package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +0 -734
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +0 -468
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +0 -1154
- package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +0 -146
- package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +0 -114
- package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +0 -288
- package/src/components/feedback/Dialog/Dialog.stories.tsx +0 -833
- package/src/components/feedback/Dialog/Dialog.test.stories.tsx +0 -964
- package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +0 -66
- package/src/components/feedback/Modal/Modal.stories.tsx +0 -765
- package/src/components/feedback/Modal/Modal.test.stories.tsx +0 -251
- package/src/components/feedback/Sonner/Sonner.stories.tsx +0 -698
- package/src/components/feedback/Sonner/Sonner.test.stories.tsx +0 -896
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +0 -1783
- package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +0 -973
- package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +0 -104
- package/src/components/feedback/Toast/Toast.stories.tsx +0 -548
- package/src/components/feedback/Toast/Toast.test.stories.tsx +0 -834
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +0 -691
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +0 -634
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +0 -665
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +0 -727
- package/src/components/form/Autocomplete/Autocomplete.stories.tsx +0 -715
- package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +0 -825
- package/src/components/form/Button/Button.stories.tsx +0 -365
- package/src/components/form/Button/Button.test.stories.tsx +0 -607
- package/src/components/form/Calendar/Calendar.stories.tsx +0 -349
- package/src/components/form/Calendar/Calendar.test.stories.tsx +0 -464
- package/src/components/form/CepField/CepField.stories.tsx +0 -138
- package/src/components/form/CepField/CepField.test.stories.tsx +0 -281
- package/src/components/form/Checkbox/Checkbox.stories.tsx +0 -265
- package/src/components/form/Checkbox/Checkbox.test.stories.tsx +0 -181
- package/src/components/form/CodeEditor/CodeEditor.stories.tsx +0 -871
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +0 -574
- package/src/components/form/Command/Command.stories.tsx +0 -580
- package/src/components/form/Command/Command.test.stories.tsx +0 -215
- package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +0 -112
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +0 -134
- package/src/components/form/Form/Form.stories.tsx +0 -687
- package/src/components/form/Form/Form.test.stories.tsx +0 -601
- package/src/components/form/Input/Input.stories.tsx +0 -260
- package/src/components/form/Input/Input.test.stories.tsx +0 -1191
- package/src/components/form/InputOTP/InputOTP.stories.tsx +0 -774
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +0 -588
- package/src/components/form/Label/Label.stories.tsx +0 -473
- package/src/components/form/Label/Label.test.stories.tsx +0 -915
- package/src/components/form/Menubar/Menubar.stories.tsx +0 -472
- package/src/components/form/Menubar/Menubar.test.stories.tsx +0 -584
- package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +0 -562
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +0 -327
- package/src/components/form/PhoneInput/PhoneInput.stories.tsx +0 -495
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +0 -666
- package/src/components/form/RadioGroup/RadioGroup.stories.tsx +0 -518
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +0 -685
- package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +0 -261
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +0 -491
- package/src/components/form/SearchPalette/SearchPalette.stories.tsx +0 -135
- package/src/components/form/Select/Select.stories.tsx +0 -364
- package/src/components/form/Select/Select.test.stories.tsx +0 -985
- package/src/components/form/Slider/Slider.stories.tsx +0 -418
- package/src/components/form/Slider/Slider.test.stories.tsx +0 -1042
- package/src/components/form/Switch/Switch.stories.tsx +0 -697
- package/src/components/form/Switch/Switch.test.stories.tsx +0 -597
- package/src/components/form/Textarea/Textarea.stories.tsx +0 -473
- package/src/components/form/Textarea/Textarea.test.stories.tsx +0 -702
- package/src/components/form/Toggle/Toggle.stories.tsx +0 -638
- package/src/components/form/Toggle/Toggle.test.stories.tsx +0 -1159
- package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +0 -617
- package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +0 -915
- package/src/components/form/UploadButton/UploadButton.stories.tsx +0 -367
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +0 -573
- package/src/components/form/total-form/total-form.test.tsx +0 -214
- package/src/components/layout/Accordion/Accordion.stories.tsx +0 -694
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +0 -1344
- package/src/components/layout/Card/Card.stories.tsx +0 -591
- package/src/components/layout/Card/Card.test.stories.tsx +0 -1134
- package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +0 -92
- package/src/components/layout/Collapsible/Collapsible.stories.tsx +0 -180
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +0 -308
- package/src/components/layout/Container/Container.stories.tsx +0 -557
- package/src/components/layout/Container/Container.test.stories.tsx +0 -745
- package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +0 -129
- package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +0 -251
- package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +0 -39
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +0 -131
- package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +0 -99
- package/src/components/layout/Dashboard/Dashboard.stories.tsx +0 -104
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +0 -190
- package/src/components/layout/Drawer/Drawer.stories.tsx +0 -658
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +0 -1343
- package/src/components/layout/Resizable/Resizable.stories.tsx +0 -1039
- package/src/components/layout/Resizable/Resizable.test.stories.tsx +0 -754
- package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +0 -651
- package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +0 -619
- package/src/components/layout/Separator/Separator.stories.tsx +0 -563
- package/src/components/layout/Separator/Separator.test.stories.tsx +0 -518
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +0 -192
- package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +0 -175
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +0 -609
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +0 -1324
- package/src/components/layout/Skeleton/Skeleton.stories.tsx +0 -689
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +0 -521
- package/src/components/layout/Spacer/Spacer.stories.tsx +0 -502
- package/src/components/layout/Spacer/Spacer.test.stories.tsx +0 -474
- package/src/components/layout/TableFilter/TableFilter.stories.tsx +0 -82
- package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +0 -87
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +0 -638
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +0 -1008
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +0 -386
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +0 -435
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +0 -614
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +0 -845
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +0 -578
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +0 -660
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +0 -516
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +0 -935
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +0 -501
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +0 -453
- package/src/components/navigation/Pagination/Pagination.stories.tsx +0 -602
- package/src/components/navigation/Pagination/Pagination.test.stories.tsx +0 -520
- package/src/components/navigation/Tabs/Tabs.stories.tsx +0 -1004
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +0 -1225
- package/src/components/typography/Blockquote/Blockquote.stories.tsx +0 -359
- package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +0 -432
- package/src/components/typography/Code/Code.stories.tsx +0 -686
- package/src/components/typography/Code/Code.test.stories.tsx +0 -703
- package/src/components/typography/Heading/Heading.stories.tsx +0 -451
- package/src/components/typography/Heading/Heading.test.stories.tsx +0 -830
- package/src/components/typography/Paragraph/Paragraph.stories.tsx +0 -280
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +0 -231
- package/src/components/typography/Text/Text.stories.tsx +0 -359
- package/src/components/typography/Text/Text.test.stories.tsx +0 -759
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +0 -945
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +0 -603
- package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +0 -414
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +0 -859
- package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +0 -620
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +0 -361
- package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +0 -118
- package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +0 -83
- package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +0 -65
- package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +0 -271
- package/src/components/utility/Portal/Portal.stories.tsx +0 -340
- package/src/components/utility/Portal/Portal.test.stories.tsx +0 -2086
- package/src/components/utility/Transition/Transition.stories.tsx +0 -493
- package/src/components/utility/Transition/Transition.test.stories.tsx +0 -854
- package/src/components/utility/VirtualList/VirtualList.stories.tsx +0 -1073
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +0 -1085
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +0 -431
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +0 -364
- /package/src/{button.tsx → button/button.tsx} +0 -0
|
@@ -1,2070 +0,0 @@
|
|
|
1
|
-
import { Box, Button, Stack,Typography } from '@mui/material';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { expect, userEvent, waitFor,within } from 'storybook/test';
|
|
5
|
-
|
|
6
|
-
import { Progress } from './Progress';
|
|
7
|
-
|
|
8
|
-
const meta: Meta<typeof Progress> = {
|
|
9
|
-
title: 'DataDisplay/Progress/Tests',
|
|
10
|
-
component: Progress,
|
|
11
|
-
parameters: {
|
|
12
|
-
layout: 'centered',
|
|
13
|
-
chromatic: { disableSnapshot: false },
|
|
14
|
-
},
|
|
15
|
-
tags: ['autodocs', 'test', 'component:Progress', 'checked'],
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export default meta;
|
|
19
|
-
type Story = StoryObj<typeof meta>;
|
|
20
|
-
|
|
21
|
-
// Basic Interaction Tests
|
|
22
|
-
export const BasicInteraction: Story = {
|
|
23
|
-
name: '🧪 Basic Interaction Test',
|
|
24
|
-
args: {
|
|
25
|
-
value: 50,
|
|
26
|
-
showLabel: true,
|
|
27
|
-
},
|
|
28
|
-
render: (args) => (
|
|
29
|
-
<Box data-testid="progress-container" sx={{ width: 300, p: 2 }}>
|
|
30
|
-
<Progress {...args} data-testid="progress-component" />
|
|
31
|
-
<Typography variant="caption" sx={{ mt: 1, display: 'block' }}>
|
|
32
|
-
Progress: {args.value}%
|
|
33
|
-
</Typography>
|
|
34
|
-
</Box>
|
|
35
|
-
),
|
|
36
|
-
play: async ({ canvasElement, step }) => {
|
|
37
|
-
const canvas = within(canvasElement);
|
|
38
|
-
|
|
39
|
-
await step('Initial render verification', async () => {
|
|
40
|
-
const container = canvas.getByTestId('progress-container');
|
|
41
|
-
await expect(container).toBeInTheDocument();
|
|
42
|
-
|
|
43
|
-
const progressComponent = canvas.getByTestId('progress-component');
|
|
44
|
-
await expect(progressComponent).toBeInTheDocument();
|
|
45
|
-
|
|
46
|
-
// Check for progress value display
|
|
47
|
-
const label = canvas.getByText('50%');
|
|
48
|
-
await expect(label).toBeInTheDocument();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await step('Progress component properties', async () => {
|
|
52
|
-
const progressComponent = canvas.getByTestId('progress-component');
|
|
53
|
-
await expect(progressComponent).toHaveAttribute('data-testid', 'progress-component');
|
|
54
|
-
|
|
55
|
-
// Verify progress bar is visible
|
|
56
|
-
const progressBar = progressComponent.querySelector('.MuiLinearProgress-bar');
|
|
57
|
-
await expect(progressBar).toBeInTheDocument();
|
|
58
|
-
|
|
59
|
-
// Check that progress value is reflected in the bar's style
|
|
60
|
-
if (progressBar) {
|
|
61
|
-
const computedStyle = window.getComputedStyle(progressBar);
|
|
62
|
-
const transform = computedStyle.transform;
|
|
63
|
-
|
|
64
|
-
// Progress bar should have some transform or width for determinate state
|
|
65
|
-
// MUI LinearProgress may use different methods (transform or width)
|
|
66
|
-
if (transform && transform !== 'none') {
|
|
67
|
-
// Transform is being used
|
|
68
|
-
expect(transform).toBeTruthy();
|
|
69
|
-
|
|
70
|
-
// Just verify it has a transform, don't assume specific values
|
|
71
|
-
// as MUI implementation may vary
|
|
72
|
-
if (transform.includes('matrix')) {
|
|
73
|
-
const matrixValues = transform.match(/matrix\(([^)]+)\)/);
|
|
74
|
-
expect(matrixValues).toBeTruthy();
|
|
75
|
-
} else if (transform.includes('translateX') || transform.includes('scaleX')) {
|
|
76
|
-
// Has some form of transform
|
|
77
|
-
expect(transform).toMatch(/translateX|scaleX/);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Also check if width is being used instead of transform
|
|
82
|
-
const width = computedStyle.width;
|
|
83
|
-
if (width && width !== 'auto' && width !== '100%') {
|
|
84
|
-
// Width-based progress indication
|
|
85
|
-
expect(width).toBeTruthy();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
await step('Visual state verification', async () => {
|
|
91
|
-
const progressComponent = canvas.getByTestId('progress-component');
|
|
92
|
-
const computedStyle = window.getComputedStyle(progressComponent);
|
|
93
|
-
|
|
94
|
-
// Should be visible and have proper dimensions
|
|
95
|
-
await expect(computedStyle.display).not.toBe('none');
|
|
96
|
-
await expect(progressComponent).toBeVisible();
|
|
97
|
-
});
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// Form Interaction Test (Progress can be controlled)
|
|
102
|
-
const FormInteractionComponent = () => {
|
|
103
|
-
const [progress, setProgress] = React.useState(0);
|
|
104
|
-
const [isRunning, setIsRunning] = React.useState(false);
|
|
105
|
-
|
|
106
|
-
const startProgress = () => {
|
|
107
|
-
setIsRunning(true);
|
|
108
|
-
setProgress(0);
|
|
109
|
-
|
|
110
|
-
const interval = window.setInterval(() => {
|
|
111
|
-
setProgress((prev) => {
|
|
112
|
-
if (prev >= 100) {
|
|
113
|
-
window.clearInterval(interval);
|
|
114
|
-
setIsRunning(false);
|
|
115
|
-
return 100;
|
|
116
|
-
}
|
|
117
|
-
return prev + 10;
|
|
118
|
-
});
|
|
119
|
-
}, 100);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const resetProgress = () => {
|
|
123
|
-
setProgress(0);
|
|
124
|
-
setIsRunning(false);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<Box sx={{ width: 400, p: 2 }}>
|
|
129
|
-
<Progress value={progress} showLabel data-testid="controllable-progress" color="primary" />
|
|
130
|
-
<Box sx={{ mt: 2, display: 'flex', gap: 1 }}>
|
|
131
|
-
<Button
|
|
132
|
-
variant="contained"
|
|
133
|
-
onClick={startProgress}
|
|
134
|
-
disabled={isRunning}
|
|
135
|
-
data-testid="start-button"
|
|
136
|
-
>
|
|
137
|
-
Start
|
|
138
|
-
</Button>
|
|
139
|
-
<Button variant="outlined" onClick={resetProgress} data-testid="reset-button">
|
|
140
|
-
Reset
|
|
141
|
-
</Button>
|
|
142
|
-
</Box>
|
|
143
|
-
<Typography variant="caption" sx={{ mt: 1, display: 'block' }}>
|
|
144
|
-
Status: {isRunning ? 'Running' : 'Stopped'}
|
|
145
|
-
</Typography>
|
|
146
|
-
</Box>
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
export const FormInteraction: Story = {
|
|
151
|
-
name: '📝 Form Interaction Test',
|
|
152
|
-
render: () => <FormInteractionComponent />,
|
|
153
|
-
play: async ({ canvasElement, step }) => {
|
|
154
|
-
const canvas = within(canvasElement);
|
|
155
|
-
|
|
156
|
-
await step('Initial state verification', async () => {
|
|
157
|
-
const progressComponent = canvas.getByTestId('controllable-progress');
|
|
158
|
-
await expect(progressComponent).toBeInTheDocument();
|
|
159
|
-
|
|
160
|
-
const startButton = canvas.getByTestId('start-button');
|
|
161
|
-
const resetButton = canvas.getByTestId('reset-button');
|
|
162
|
-
|
|
163
|
-
await expect(startButton).toBeEnabled();
|
|
164
|
-
await expect(resetButton).toBeEnabled();
|
|
165
|
-
|
|
166
|
-
// Should show 0% initially
|
|
167
|
-
const progressContainer = canvas.getByTestId('controllable-progress').parentElement;
|
|
168
|
-
await expect(progressContainer).toHaveTextContent('0%');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
await step('Start progress interaction', async () => {
|
|
172
|
-
const startButton = canvas.getByTestId('start-button');
|
|
173
|
-
await userEvent.click(startButton);
|
|
174
|
-
|
|
175
|
-
// Button should become disabled when running
|
|
176
|
-
await expect(startButton).toBeDisabled();
|
|
177
|
-
|
|
178
|
-
// Status should change to running
|
|
179
|
-
await waitFor(
|
|
180
|
-
() => {
|
|
181
|
-
const status = canvas.getByText('Status: Running');
|
|
182
|
-
expect(status).toBeInTheDocument();
|
|
183
|
-
},
|
|
184
|
-
{ timeout: 2000 },
|
|
185
|
-
);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
await step('Progress value changes verification', async () => {
|
|
189
|
-
// Store initial transform value
|
|
190
|
-
const progressComponent = canvas.getByTestId('controllable-progress');
|
|
191
|
-
const progressBar = progressComponent.querySelector('.MuiLinearProgress-bar');
|
|
192
|
-
let previousTransform = '';
|
|
193
|
-
|
|
194
|
-
if (progressBar) {
|
|
195
|
-
previousTransform = window.getComputedStyle(progressBar).transform;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Wait for progress to start changing
|
|
199
|
-
await waitFor(
|
|
200
|
-
() => {
|
|
201
|
-
const progressBar = progressComponent.querySelector('.MuiLinearProgress-bar');
|
|
202
|
-
|
|
203
|
-
if (progressBar) {
|
|
204
|
-
const computedStyle = window.getComputedStyle(progressBar);
|
|
205
|
-
const currentTransform = computedStyle.transform;
|
|
206
|
-
|
|
207
|
-
// Transform or style should be changing as progress updates
|
|
208
|
-
if (currentTransform && currentTransform !== 'none') {
|
|
209
|
-
expect(currentTransform).not.toBe(previousTransform);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Also check width changes as an alternative progress indicator
|
|
213
|
-
const currentWidth = computedStyle.width;
|
|
214
|
-
if (currentWidth && currentWidth !== 'auto') {
|
|
215
|
-
// Just verify something is changing
|
|
216
|
-
expect(currentTransform !== previousTransform || currentWidth).toBeTruthy();
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
{ timeout: 3000 },
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
// Check that progress values are changing by verifying multiple intermediate values
|
|
224
|
-
const seenValues = new Set();
|
|
225
|
-
await waitFor(
|
|
226
|
-
() => {
|
|
227
|
-
const progressContainer = canvas.getByTestId('controllable-progress').parentElement;
|
|
228
|
-
const textContent = progressContainer?.textContent || '';
|
|
229
|
-
|
|
230
|
-
// Extract percentage value
|
|
231
|
-
const percentMatch = textContent.match(/(\d+)%/);
|
|
232
|
-
if (percentMatch) {
|
|
233
|
-
const value = parseInt(percentMatch[1]);
|
|
234
|
-
seenValues.add(value);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// We should see at least 3 different values during animation
|
|
238
|
-
expect(seenValues.size).toBeGreaterThanOrEqual(3);
|
|
239
|
-
},
|
|
240
|
-
{ timeout: 3000 },
|
|
241
|
-
);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
await step('Progress completion', async () => {
|
|
245
|
-
// Wait for progress to complete
|
|
246
|
-
await waitFor(
|
|
247
|
-
() => {
|
|
248
|
-
const completedLabel = canvas.queryByText('100%');
|
|
249
|
-
expect(completedLabel).toBeInTheDocument();
|
|
250
|
-
},
|
|
251
|
-
{ timeout: 5000 },
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
// Verify the progress bar shows full completion
|
|
255
|
-
const progressComponent = canvas.getByTestId('controllable-progress');
|
|
256
|
-
const progressBar = progressComponent.querySelector('.MuiLinearProgress-bar');
|
|
257
|
-
|
|
258
|
-
if (progressBar) {
|
|
259
|
-
const computedStyle = window.getComputedStyle(progressBar);
|
|
260
|
-
const transform = computedStyle.transform;
|
|
261
|
-
|
|
262
|
-
// At 100%, the progress should be complete
|
|
263
|
-
// MUI may use transform or width to show this
|
|
264
|
-
if (transform && transform !== 'none') {
|
|
265
|
-
expect(transform).toBeTruthy();
|
|
266
|
-
// Just verify it has changed from initial state
|
|
267
|
-
} else {
|
|
268
|
-
// Check if width indicates 100%
|
|
269
|
-
const width = computedStyle.width;
|
|
270
|
-
if (width) {
|
|
271
|
-
expect(width).toBeTruthy();
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Button should be re-enabled after completion
|
|
277
|
-
await waitFor(
|
|
278
|
-
() => {
|
|
279
|
-
const startButton = canvas.getByTestId('start-button');
|
|
280
|
-
expect(startButton).toBeEnabled();
|
|
281
|
-
},
|
|
282
|
-
{ timeout: 1000 },
|
|
283
|
-
);
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
await step('Reset functionality', async () => {
|
|
287
|
-
const resetButton = canvas.getByTestId('reset-button');
|
|
288
|
-
await userEvent.click(resetButton);
|
|
289
|
-
|
|
290
|
-
// Should reset to 0%
|
|
291
|
-
await waitFor(() => {
|
|
292
|
-
const progressContainer = canvas.getByTestId('controllable-progress').parentElement;
|
|
293
|
-
expect(progressContainer).toHaveTextContent('0%');
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// Status should change to stopped
|
|
297
|
-
const status = canvas.getByText('Status: Stopped');
|
|
298
|
-
await expect(status).toBeInTheDocument();
|
|
299
|
-
});
|
|
300
|
-
},
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// Keyboard Navigation Test
|
|
304
|
-
export const KeyboardNavigation: Story = {
|
|
305
|
-
name: '⌨️ Keyboard Navigation Test',
|
|
306
|
-
args: {
|
|
307
|
-
value: 60,
|
|
308
|
-
showLabel: true,
|
|
309
|
-
},
|
|
310
|
-
parameters: {
|
|
311
|
-
a11y: {
|
|
312
|
-
element: '#storybook-root',
|
|
313
|
-
config: {
|
|
314
|
-
rules: [
|
|
315
|
-
{ id: 'color-contrast', enabled: true },
|
|
316
|
-
{ id: 'aria-required-attr', enabled: true },
|
|
317
|
-
{ id: 'aria-roles', enabled: true },
|
|
318
|
-
{ id: 'aria-valid-attr-value', enabled: true },
|
|
319
|
-
{ id: 'duplicate-id', enabled: true },
|
|
320
|
-
{ id: 'label', enabled: true },
|
|
321
|
-
],
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
render: (args) => (
|
|
326
|
-
<Box sx={{ width: 400, p: 2 }}>
|
|
327
|
-
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
328
|
-
Progress Accessibility Test
|
|
329
|
-
</Typography>
|
|
330
|
-
<Button data-testid="before-progress" tabIndex={0}>
|
|
331
|
-
Before Progress
|
|
332
|
-
</Button>
|
|
333
|
-
<Box sx={{ my: 2 }}>
|
|
334
|
-
<Progress
|
|
335
|
-
{...args}
|
|
336
|
-
data-testid="progress-keyboard"
|
|
337
|
-
role="progressbar"
|
|
338
|
-
aria-valuemin={0}
|
|
339
|
-
aria-valuemax={100}
|
|
340
|
-
aria-valuenow={args.value}
|
|
341
|
-
aria-label="Loading progress"
|
|
342
|
-
/>
|
|
343
|
-
</Box>
|
|
344
|
-
<Button data-testid="after-progress" tabIndex={0}>
|
|
345
|
-
After Progress
|
|
346
|
-
</Button>
|
|
347
|
-
</Box>
|
|
348
|
-
),
|
|
349
|
-
play: async ({ canvasElement, step }) => {
|
|
350
|
-
const canvas = within(canvasElement);
|
|
351
|
-
|
|
352
|
-
await step('Progress accessibility attributes', async () => {
|
|
353
|
-
const progressComponent = canvas.getByTestId('progress-keyboard');
|
|
354
|
-
|
|
355
|
-
// Check ARIA attributes
|
|
356
|
-
await expect(progressComponent).toHaveAttribute('role', 'progressbar');
|
|
357
|
-
await expect(progressComponent).toHaveAttribute('aria-valuemin', '0');
|
|
358
|
-
await expect(progressComponent).toHaveAttribute('aria-valuemax', '100');
|
|
359
|
-
await expect(progressComponent).toHaveAttribute('aria-valuenow', '60');
|
|
360
|
-
await expect(progressComponent).toHaveAttribute('aria-label', 'Loading progress');
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
await step('Tab navigation sequence', async () => {
|
|
364
|
-
const beforeButton = canvas.getByTestId('before-progress');
|
|
365
|
-
const afterButton = canvas.getByTestId('after-progress');
|
|
366
|
-
|
|
367
|
-
// Start from before button
|
|
368
|
-
beforeButton.focus();
|
|
369
|
-
await expect(beforeButton).toHaveFocus();
|
|
370
|
-
|
|
371
|
-
// Tab to after button (progress should be skipped as it's not interactive)
|
|
372
|
-
await userEvent.tab();
|
|
373
|
-
await expect(afterButton).toHaveFocus();
|
|
374
|
-
|
|
375
|
-
// Tab backward
|
|
376
|
-
await userEvent.tab({ shift: true });
|
|
377
|
-
await expect(beforeButton).toHaveFocus();
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
await step('Screen reader compatibility', async () => {
|
|
381
|
-
const progressComponent = canvas.getByTestId('progress-keyboard');
|
|
382
|
-
|
|
383
|
-
// Should be findable by screen reader via role
|
|
384
|
-
const progressByRole = canvas.getByRole('progressbar');
|
|
385
|
-
await expect(progressByRole).toBe(progressComponent);
|
|
386
|
-
|
|
387
|
-
// Should be findable by accessible name
|
|
388
|
-
const progressByLabel = canvas.getByLabelText('Loading progress');
|
|
389
|
-
await expect(progressByLabel).toBe(progressComponent);
|
|
390
|
-
});
|
|
391
|
-
},
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
// Screen Reader Test
|
|
395
|
-
export const ScreenReader: Story = {
|
|
396
|
-
name: '🔊 Screen Reader Test',
|
|
397
|
-
render: () => (
|
|
398
|
-
<Box sx={{ width: 500, p: 2 }}>
|
|
399
|
-
<Typography variant="h6" sx={{ mb: 2 }} id="progress-section">
|
|
400
|
-
File Upload Progress
|
|
401
|
-
</Typography>
|
|
402
|
-
|
|
403
|
-
<Progress
|
|
404
|
-
value={75}
|
|
405
|
-
showLabel
|
|
406
|
-
data-testid="screen-reader-progress"
|
|
407
|
-
role="progressbar"
|
|
408
|
-
aria-valuemin={0}
|
|
409
|
-
aria-valuemax={100}
|
|
410
|
-
aria-valuenow={75}
|
|
411
|
-
aria-label="File upload progress"
|
|
412
|
-
aria-describedby="progress-description"
|
|
413
|
-
/>
|
|
414
|
-
|
|
415
|
-
<Typography
|
|
416
|
-
id="progress-description"
|
|
417
|
-
variant="body2"
|
|
418
|
-
sx={{ mt: 1 }}
|
|
419
|
-
data-testid="progress-description"
|
|
420
|
-
>
|
|
421
|
-
Uploading document.pdf - 3 of 4 files completed
|
|
422
|
-
</Typography>
|
|
423
|
-
|
|
424
|
-
<div role="status" aria-live="polite" data-testid="live-region">
|
|
425
|
-
Upload progress: 75% complete
|
|
426
|
-
</div>
|
|
427
|
-
|
|
428
|
-
{/* Different progress states for screen reader testing */}
|
|
429
|
-
<Box sx={{ mt: 3 }}>
|
|
430
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
431
|
-
Loading State
|
|
432
|
-
</Typography>
|
|
433
|
-
<Progress
|
|
434
|
-
data-testid="indeterminate-progress"
|
|
435
|
-
role="progressbar"
|
|
436
|
-
aria-label="Loading content"
|
|
437
|
-
aria-describedby="loading-description"
|
|
438
|
-
/>
|
|
439
|
-
<Typography id="loading-description" variant="caption" sx={{ display: 'block', mt: 0.5 }}>
|
|
440
|
-
Please wait while content loads...
|
|
441
|
-
</Typography>
|
|
442
|
-
</Box>
|
|
443
|
-
</Box>
|
|
444
|
-
),
|
|
445
|
-
play: async ({ canvasElement, step }) => {
|
|
446
|
-
const canvas = within(canvasElement);
|
|
447
|
-
|
|
448
|
-
await step('ARIA labels verification', async () => {
|
|
449
|
-
const progressComponent = canvas.getByTestId('screen-reader-progress');
|
|
450
|
-
|
|
451
|
-
await expect(progressComponent).toHaveAttribute('aria-label', 'File upload progress');
|
|
452
|
-
await expect(progressComponent).toHaveAttribute('aria-describedby', 'progress-description');
|
|
453
|
-
await expect(progressComponent).toHaveAttribute('role', 'progressbar');
|
|
454
|
-
|
|
455
|
-
// Should be findable by screen readers
|
|
456
|
-
const progressByLabel = canvas.getByLabelText('File upload progress');
|
|
457
|
-
await expect(progressByLabel).toBe(progressComponent);
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
await step('ARIA descriptions verification', async () => {
|
|
461
|
-
const description = canvas.getByTestId('progress-description');
|
|
462
|
-
await expect(description).toBeInTheDocument();
|
|
463
|
-
await expect(description).toHaveAttribute('id', 'progress-description');
|
|
464
|
-
|
|
465
|
-
const descriptionText = canvas.getByText(/Uploading document.pdf/);
|
|
466
|
-
await expect(descriptionText).toBeInTheDocument();
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
await step('Live region verification', async () => {
|
|
470
|
-
const liveRegion = canvas.getByTestId('live-region');
|
|
471
|
-
await expect(liveRegion).toHaveAttribute('role', 'status');
|
|
472
|
-
await expect(liveRegion).toHaveAttribute('aria-live', 'polite');
|
|
473
|
-
|
|
474
|
-
const liveText = canvas.getByText('Upload progress: 75% complete');
|
|
475
|
-
await expect(liveText).toBeInTheDocument();
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
await step('Indeterminate progress accessibility', async () => {
|
|
479
|
-
const indeterminateProgress = canvas.getByTestId('indeterminate-progress');
|
|
480
|
-
|
|
481
|
-
await expect(indeterminateProgress).toHaveAttribute('role', 'progressbar');
|
|
482
|
-
await expect(indeterminateProgress).toHaveAttribute('aria-label', 'Loading content');
|
|
483
|
-
await expect(indeterminateProgress).toHaveAttribute(
|
|
484
|
-
'aria-describedby',
|
|
485
|
-
'loading-description',
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
// For indeterminate progress, aria-valuenow should not be set
|
|
489
|
-
await expect(indeterminateProgress).not.toHaveAttribute('aria-valuenow');
|
|
490
|
-
|
|
491
|
-
// Verify indeterminate animation is active
|
|
492
|
-
const indeterminateBar = indeterminateProgress.querySelector(
|
|
493
|
-
'.MuiLinearProgress-bar1Indeterminate, .MuiLinearProgress-bar',
|
|
494
|
-
);
|
|
495
|
-
if (indeterminateBar) {
|
|
496
|
-
const computedStyle = window.getComputedStyle(indeterminateBar);
|
|
497
|
-
// Should have animation for indeterminate state
|
|
498
|
-
expect(computedStyle.animation || computedStyle.animationName).not.toBe('none');
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
await step('Progress role verification', async () => {
|
|
503
|
-
// All progress elements should be findable by role
|
|
504
|
-
const allProgressBars = canvas.getAllByRole('progressbar');
|
|
505
|
-
await expect(allProgressBars).toHaveLength(2);
|
|
506
|
-
|
|
507
|
-
// Each should have appropriate labeling
|
|
508
|
-
allProgressBars.forEach(async (progress) => {
|
|
509
|
-
const hasLabel =
|
|
510
|
-
progress.hasAttribute('aria-label') || progress.hasAttribute('aria-labelledby');
|
|
511
|
-
await expect(hasLabel).toBe(true);
|
|
512
|
-
});
|
|
513
|
-
});
|
|
514
|
-
},
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
// Focus Management Test
|
|
518
|
-
const FocusManagementComponent = () => {
|
|
519
|
-
const [showProgress, setShowProgress] = React.useState(false);
|
|
520
|
-
const [progress, setProgress] = React.useState(0);
|
|
521
|
-
|
|
522
|
-
const startTask = () => {
|
|
523
|
-
setShowProgress(true);
|
|
524
|
-
setProgress(0);
|
|
525
|
-
|
|
526
|
-
const interval = window.setInterval(() => {
|
|
527
|
-
setProgress((prev) => {
|
|
528
|
-
if (prev >= 100) {
|
|
529
|
-
window.clearInterval(interval);
|
|
530
|
-
return 100;
|
|
531
|
-
}
|
|
532
|
-
return prev + 20;
|
|
533
|
-
});
|
|
534
|
-
}, 500);
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
return (
|
|
538
|
-
<Box sx={{ width: 400, p: 2 }}>
|
|
539
|
-
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
540
|
-
Focus Management Demo
|
|
541
|
-
</Typography>
|
|
542
|
-
|
|
543
|
-
<Button
|
|
544
|
-
data-testid="trigger-button"
|
|
545
|
-
variant="contained"
|
|
546
|
-
onClick={startTask}
|
|
547
|
-
disabled={showProgress}
|
|
548
|
-
>
|
|
549
|
-
Start Task
|
|
550
|
-
</Button>
|
|
551
|
-
|
|
552
|
-
{showProgress && (
|
|
553
|
-
<Box sx={{ mt: 2 }} data-testid="progress-section">
|
|
554
|
-
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
555
|
-
Task in progress...
|
|
556
|
-
</Typography>
|
|
557
|
-
<Progress
|
|
558
|
-
value={progress}
|
|
559
|
-
showLabel
|
|
560
|
-
data-testid="focus-progress"
|
|
561
|
-
role="progressbar"
|
|
562
|
-
aria-label="Task progress"
|
|
563
|
-
aria-valuemin={0}
|
|
564
|
-
aria-valuemax={100}
|
|
565
|
-
aria-valuenow={progress}
|
|
566
|
-
/>
|
|
567
|
-
|
|
568
|
-
{progress === 100 && (
|
|
569
|
-
<Box sx={{ mt: 2 }}>
|
|
570
|
-
<Typography variant="body2" color="success.main" sx={{ mb: 1 }}>
|
|
571
|
-
Task completed successfully!
|
|
572
|
-
</Typography>
|
|
573
|
-
<Button
|
|
574
|
-
data-testid="completion-button"
|
|
575
|
-
variant="outlined"
|
|
576
|
-
onClick={() => setShowProgress(false)}
|
|
577
|
-
autoFocus
|
|
578
|
-
>
|
|
579
|
-
Continue
|
|
580
|
-
</Button>
|
|
581
|
-
</Box>
|
|
582
|
-
)}
|
|
583
|
-
</Box>
|
|
584
|
-
)}
|
|
585
|
-
</Box>
|
|
586
|
-
);
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
export const FocusManagement: Story = {
|
|
590
|
-
name: '🎯 Focus Management Test',
|
|
591
|
-
render: () => <FocusManagementComponent />,
|
|
592
|
-
play: async ({ canvasElement, step }) => {
|
|
593
|
-
const canvas = within(canvasElement);
|
|
594
|
-
|
|
595
|
-
await step('Initial focus state', async () => {
|
|
596
|
-
const triggerButton = canvas.getByTestId('trigger-button');
|
|
597
|
-
await expect(triggerButton).toBeInTheDocument();
|
|
598
|
-
await expect(triggerButton).toBeEnabled();
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
await step('Progress appearance and focus', async () => {
|
|
602
|
-
const triggerButton = canvas.getByTestId('trigger-button');
|
|
603
|
-
triggerButton.focus();
|
|
604
|
-
await expect(triggerButton).toHaveFocus();
|
|
605
|
-
|
|
606
|
-
await userEvent.click(triggerButton);
|
|
607
|
-
|
|
608
|
-
// Progress should appear
|
|
609
|
-
await waitFor(() => {
|
|
610
|
-
const progressSection = canvas.getByTestId('progress-section');
|
|
611
|
-
expect(progressSection).toBeInTheDocument();
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
const progressComponent = canvas.getByTestId('focus-progress');
|
|
615
|
-
await expect(progressComponent).toBeInTheDocument();
|
|
616
|
-
|
|
617
|
-
// Trigger button should be disabled
|
|
618
|
-
await expect(triggerButton).toBeDisabled();
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
await step('Progress completion and focus restoration', async () => {
|
|
622
|
-
// Wait for progress to complete
|
|
623
|
-
await waitFor(
|
|
624
|
-
() => {
|
|
625
|
-
const completionMessage = canvas.getByText('Task completed successfully!');
|
|
626
|
-
expect(completionMessage).toBeInTheDocument();
|
|
627
|
-
},
|
|
628
|
-
{ timeout: 8000 },
|
|
629
|
-
);
|
|
630
|
-
|
|
631
|
-
// Continue button should appear with auto focus
|
|
632
|
-
const continueButton = canvas.getByTestId('completion-button');
|
|
633
|
-
await expect(continueButton).toBeInTheDocument();
|
|
634
|
-
|
|
635
|
-
await waitFor(
|
|
636
|
-
() => {
|
|
637
|
-
expect(continueButton).toHaveFocus();
|
|
638
|
-
},
|
|
639
|
-
{ timeout: 1000 },
|
|
640
|
-
);
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
await step('Focus restoration after cleanup', async () => {
|
|
644
|
-
const continueButton = canvas.getByTestId('completion-button');
|
|
645
|
-
await userEvent.click(continueButton);
|
|
646
|
-
|
|
647
|
-
// Progress section should disappear
|
|
648
|
-
await waitFor(() => {
|
|
649
|
-
const progressSection = canvas.queryByTestId('progress-section');
|
|
650
|
-
expect(progressSection).not.toBeInTheDocument();
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
// Trigger button should be enabled again
|
|
654
|
-
const triggerButton = canvas.getByTestId('trigger-button');
|
|
655
|
-
await expect(triggerButton).toBeEnabled();
|
|
656
|
-
});
|
|
657
|
-
},
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
// Responsive Design Test
|
|
661
|
-
export const ResponsiveDesign: Story = {
|
|
662
|
-
name: '📱 Responsive Design Test',
|
|
663
|
-
parameters: {
|
|
664
|
-
viewport: {
|
|
665
|
-
viewports: {
|
|
666
|
-
mobile: {
|
|
667
|
-
name: 'Mobile',
|
|
668
|
-
styles: { width: '375px', height: '667px' },
|
|
669
|
-
type: 'mobile',
|
|
670
|
-
},
|
|
671
|
-
tablet: {
|
|
672
|
-
name: 'Tablet',
|
|
673
|
-
styles: { width: '768px', height: '1024px' },
|
|
674
|
-
type: 'tablet',
|
|
675
|
-
},
|
|
676
|
-
desktop: {
|
|
677
|
-
name: 'Desktop',
|
|
678
|
-
styles: { width: '1920px', height: '1080px' },
|
|
679
|
-
type: 'desktop',
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
defaultViewport: 'mobile',
|
|
683
|
-
},
|
|
684
|
-
chromatic: {
|
|
685
|
-
viewports: [375, 768, 1920],
|
|
686
|
-
delay: 300,
|
|
687
|
-
},
|
|
688
|
-
},
|
|
689
|
-
render: () => (
|
|
690
|
-
<Box sx={{ p: 2, maxWidth: '100%' }}>
|
|
691
|
-
<Typography variant="h6" sx={{ mb: 3 }}>
|
|
692
|
-
Responsive Progress Components
|
|
693
|
-
</Typography>
|
|
694
|
-
|
|
695
|
-
<Stack spacing={3} data-testid="responsive-container">
|
|
696
|
-
{/* Linear Progress - should adapt width */}
|
|
697
|
-
<Box>
|
|
698
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
699
|
-
Linear Progress (Full Width)
|
|
700
|
-
</Typography>
|
|
701
|
-
<Progress value={60} showLabel data-testid="responsive-linear" sx={{ width: '100%' }} />
|
|
702
|
-
</Box>
|
|
703
|
-
|
|
704
|
-
{/* Circular Progress - should center on small screens */}
|
|
705
|
-
<Box
|
|
706
|
-
sx={{
|
|
707
|
-
display: 'flex',
|
|
708
|
-
flexDirection: { xs: 'column', sm: 'row' },
|
|
709
|
-
alignItems: 'center',
|
|
710
|
-
gap: 2,
|
|
711
|
-
}}
|
|
712
|
-
>
|
|
713
|
-
<Typography variant="subtitle2">Circular Progress:</Typography>
|
|
714
|
-
<Progress variant="circular" value={75} showLabel data-testid="responsive-circular" />
|
|
715
|
-
</Box>
|
|
716
|
-
|
|
717
|
-
{/* Segmented Progress - should adapt segment sizes */}
|
|
718
|
-
<Box>
|
|
719
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
720
|
-
Segmented Progress (Adaptive)
|
|
721
|
-
</Typography>
|
|
722
|
-
<Progress
|
|
723
|
-
variant="segmented"
|
|
724
|
-
segments={10}
|
|
725
|
-
value={80}
|
|
726
|
-
showLabel
|
|
727
|
-
data-testid="responsive-segmented"
|
|
728
|
-
/>
|
|
729
|
-
</Box>
|
|
730
|
-
|
|
731
|
-
{/* Multiple sizes for different viewports */}
|
|
732
|
-
<Box
|
|
733
|
-
sx={{
|
|
734
|
-
display: 'grid',
|
|
735
|
-
gridTemplateColumns: { xs: '1fr', sm: 'repeat(3, 1fr)' },
|
|
736
|
-
gap: 2,
|
|
737
|
-
mt: 2,
|
|
738
|
-
}}
|
|
739
|
-
>
|
|
740
|
-
<Box textAlign="center">
|
|
741
|
-
<Typography variant="caption" sx={{ display: 'block', mb: 1 }}>
|
|
742
|
-
Small
|
|
743
|
-
</Typography>
|
|
744
|
-
<Progress
|
|
745
|
-
variant="circular"
|
|
746
|
-
size="sm"
|
|
747
|
-
value={40}
|
|
748
|
-
showLabel
|
|
749
|
-
data-testid="responsive-small"
|
|
750
|
-
/>
|
|
751
|
-
</Box>
|
|
752
|
-
<Box textAlign="center">
|
|
753
|
-
<Typography variant="caption" sx={{ display: 'block', mb: 1 }}>
|
|
754
|
-
Medium
|
|
755
|
-
</Typography>
|
|
756
|
-
<Progress
|
|
757
|
-
variant="circular"
|
|
758
|
-
size="md"
|
|
759
|
-
value={60}
|
|
760
|
-
showLabel
|
|
761
|
-
data-testid="responsive-medium"
|
|
762
|
-
/>
|
|
763
|
-
</Box>
|
|
764
|
-
<Box textAlign="center">
|
|
765
|
-
<Typography variant="caption" sx={{ display: 'block', mb: 1 }}>
|
|
766
|
-
Large
|
|
767
|
-
</Typography>
|
|
768
|
-
<Progress
|
|
769
|
-
variant="circular"
|
|
770
|
-
size="lg"
|
|
771
|
-
value={80}
|
|
772
|
-
showLabel
|
|
773
|
-
data-testid="responsive-large"
|
|
774
|
-
/>
|
|
775
|
-
</Box>
|
|
776
|
-
</Box>
|
|
777
|
-
</Stack>
|
|
778
|
-
</Box>
|
|
779
|
-
),
|
|
780
|
-
play: async ({ canvasElement, step }) => {
|
|
781
|
-
const canvas = within(canvasElement);
|
|
782
|
-
|
|
783
|
-
await step('Container responsiveness', async () => {
|
|
784
|
-
const container = canvas.getByTestId('responsive-container');
|
|
785
|
-
await expect(container).toBeInTheDocument();
|
|
786
|
-
|
|
787
|
-
const computedStyle = window.getComputedStyle(container);
|
|
788
|
-
await expect(computedStyle.display).toBe('flex');
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
await step('Linear progress width adaptation', async () => {
|
|
792
|
-
const linearProgress = canvas.getByTestId('responsive-linear');
|
|
793
|
-
await expect(linearProgress).toBeInTheDocument();
|
|
794
|
-
|
|
795
|
-
const computedStyle = window.getComputedStyle(linearProgress);
|
|
796
|
-
// Should take full width of container
|
|
797
|
-
expect(computedStyle.width).toBeTruthy();
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
await step('Circular progress visibility and value verification', async () => {
|
|
801
|
-
const circularProgress = canvas.getByTestId('responsive-circular');
|
|
802
|
-
await expect(circularProgress).toBeInTheDocument();
|
|
803
|
-
await expect(circularProgress).toBeVisible();
|
|
804
|
-
|
|
805
|
-
// Should maintain aspect ratio
|
|
806
|
-
const boundingBox = circularProgress.getBoundingClientRect();
|
|
807
|
-
expect(boundingBox.width).toBeGreaterThan(0);
|
|
808
|
-
expect(boundingBox.height).toBeGreaterThan(0);
|
|
809
|
-
|
|
810
|
-
// Verify actual circular progress value (75%) is reflected in stroke-dashoffset
|
|
811
|
-
const circularPath = circularProgress.querySelector('.MuiCircularProgress-circle');
|
|
812
|
-
|
|
813
|
-
if (circularPath) {
|
|
814
|
-
const computedStyle = window.getComputedStyle(circularPath);
|
|
815
|
-
const strokeDashArray = computedStyle.strokeDasharray;
|
|
816
|
-
const strokeDashOffset = computedStyle.strokeDashoffset;
|
|
817
|
-
|
|
818
|
-
// For a 75% progress, stroke-dashoffset should be about 25% of stroke-dasharray
|
|
819
|
-
if (strokeDashArray && strokeDashOffset && strokeDashArray !== 'none') {
|
|
820
|
-
const dashArrayValue = parseFloat(strokeDashArray.split(',')[0] || strokeDashArray);
|
|
821
|
-
const dashOffsetValue = parseFloat(strokeDashOffset);
|
|
822
|
-
|
|
823
|
-
// The offset should represent the remaining 25% (100% - 75%)
|
|
824
|
-
// MUI CircularProgress calculates offset as: circumference * (1 - value/100)
|
|
825
|
-
const progressRatio = 1 - dashOffsetValue / dashArrayValue;
|
|
826
|
-
expect(progressRatio).toBeCloseTo(0.75, 1); // Should be close to 75%
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
await step('Segmented progress adaptation and fill calculation', async () => {
|
|
832
|
-
const segmentedProgress = canvas.getByTestId('responsive-segmented');
|
|
833
|
-
await expect(segmentedProgress).toBeInTheDocument();
|
|
834
|
-
|
|
835
|
-
// Should contain segment elements - look for the container with display: flex
|
|
836
|
-
const segmentContainer = segmentedProgress.querySelector(
|
|
837
|
-
'div[style*="display: flex"], div div',
|
|
838
|
-
);
|
|
839
|
-
if (segmentContainer) {
|
|
840
|
-
const segments = segmentContainer.children;
|
|
841
|
-
expect(segments.length).toBe(10); // Should have exactly 10 segments
|
|
842
|
-
|
|
843
|
-
// For 80% progress with 10 segments, 8 segments should be filled
|
|
844
|
-
// Test the background colors to verify which segments are filled
|
|
845
|
-
let filledSegments = 0;
|
|
846
|
-
let emptySegments = 0;
|
|
847
|
-
|
|
848
|
-
for (let i = 0; i < segments.length; i++) {
|
|
849
|
-
const segment = segments[i] as HTMLElement;
|
|
850
|
-
const computedStyle = window.getComputedStyle(segment);
|
|
851
|
-
const backgroundColor = computedStyle.backgroundColor;
|
|
852
|
-
|
|
853
|
-
// Parse RGB values to determine if segment is filled or empty
|
|
854
|
-
// Filled segments have solid color, empty ones have low opacity (0.1)
|
|
855
|
-
if (backgroundColor && backgroundColor.includes('rgba')) {
|
|
856
|
-
const rgbaMatch = backgroundColor.match(/rgba\(([^)]+)\)/);
|
|
857
|
-
if (rgbaMatch) {
|
|
858
|
-
const values = rgbaMatch[1].split(',').map((v) => v.trim());
|
|
859
|
-
const opacity = parseFloat(values[3] || '1');
|
|
860
|
-
|
|
861
|
-
if (opacity < 0.2) {
|
|
862
|
-
emptySegments++;
|
|
863
|
-
} else {
|
|
864
|
-
filledSegments++;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
} else if (backgroundColor && backgroundColor.includes('rgb')) {
|
|
868
|
-
// Solid RGB color means filled
|
|
869
|
-
filledSegments++;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// For 80% of 10 segments, we expect exactly 8 filled and 2 empty
|
|
874
|
-
expect(filledSegments).toBe(8);
|
|
875
|
-
expect(emptySegments).toBe(2);
|
|
876
|
-
} else {
|
|
877
|
-
// If we can't find segments, just verify the component exists
|
|
878
|
-
await expect(segmentedProgress).toBeVisible();
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
await step('Size variants responsive behavior', async () => {
|
|
883
|
-
const smallProgress = canvas.getByTestId('responsive-small');
|
|
884
|
-
const mediumProgress = canvas.getByTestId('responsive-medium');
|
|
885
|
-
const largeProgress = canvas.getByTestId('responsive-large');
|
|
886
|
-
|
|
887
|
-
await expect(smallProgress).toBeVisible();
|
|
888
|
-
await expect(mediumProgress).toBeVisible();
|
|
889
|
-
await expect(largeProgress).toBeVisible();
|
|
890
|
-
|
|
891
|
-
// Each should be properly sized
|
|
892
|
-
const smallBounds = smallProgress.getBoundingClientRect();
|
|
893
|
-
const mediumBounds = mediumProgress.getBoundingClientRect();
|
|
894
|
-
const largeBounds = largeProgress.getBoundingClientRect();
|
|
895
|
-
|
|
896
|
-
expect(smallBounds.width).toBeLessThan(mediumBounds.width);
|
|
897
|
-
expect(mediumBounds.width).toBeLessThan(largeBounds.width);
|
|
898
|
-
});
|
|
899
|
-
},
|
|
900
|
-
};
|
|
901
|
-
|
|
902
|
-
// Theme Variations Test
|
|
903
|
-
export const ThemeVariations: Story = {
|
|
904
|
-
name: '🎨 Theme Variations Test',
|
|
905
|
-
parameters: {
|
|
906
|
-
backgrounds: {
|
|
907
|
-
default: 'light',
|
|
908
|
-
values: [
|
|
909
|
-
{ name: 'light', value: '#ffffff' },
|
|
910
|
-
{ name: 'dark', value: '#1a1a1a' },
|
|
911
|
-
],
|
|
912
|
-
},
|
|
913
|
-
},
|
|
914
|
-
render: () => (
|
|
915
|
-
<Box sx={{ p: 3, width: 600 }}>
|
|
916
|
-
<Typography variant="h6" sx={{ mb: 3 }}>
|
|
917
|
-
Theme Color Variations
|
|
918
|
-
</Typography>
|
|
919
|
-
|
|
920
|
-
<Stack spacing={3} data-testid="theme-container">
|
|
921
|
-
{/* Primary Colors */}
|
|
922
|
-
<Box>
|
|
923
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
924
|
-
Color Themes
|
|
925
|
-
</Typography>
|
|
926
|
-
<Stack spacing={2}>
|
|
927
|
-
<Progress color="primary" value={70} showLabel data-testid="theme-primary" />
|
|
928
|
-
<Progress color="secondary" value={70} showLabel data-testid="theme-secondary" />
|
|
929
|
-
<Progress color="success" value={70} showLabel data-testid="theme-success" />
|
|
930
|
-
<Progress color="warning" value={70} showLabel data-testid="theme-warning" />
|
|
931
|
-
<Progress color="error" value={70} showLabel data-testid="theme-error" />
|
|
932
|
-
<Progress color="neutral" value={70} showLabel data-testid="theme-neutral" />
|
|
933
|
-
</Stack>
|
|
934
|
-
</Box>
|
|
935
|
-
|
|
936
|
-
{/* Variant with theme colors */}
|
|
937
|
-
<Box>
|
|
938
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
939
|
-
Circular with Theme Colors
|
|
940
|
-
</Typography>
|
|
941
|
-
<Stack direction="row" spacing={2} justifyContent="center">
|
|
942
|
-
<Progress
|
|
943
|
-
variant="circular"
|
|
944
|
-
color="primary"
|
|
945
|
-
value={60}
|
|
946
|
-
showLabel
|
|
947
|
-
data-testid="circular-primary"
|
|
948
|
-
/>
|
|
949
|
-
<Progress
|
|
950
|
-
variant="circular"
|
|
951
|
-
color="success"
|
|
952
|
-
value={80}
|
|
953
|
-
showLabel
|
|
954
|
-
data-testid="circular-success"
|
|
955
|
-
/>
|
|
956
|
-
<Progress
|
|
957
|
-
variant="circular"
|
|
958
|
-
color="warning"
|
|
959
|
-
value={45}
|
|
960
|
-
showLabel
|
|
961
|
-
data-testid="circular-warning"
|
|
962
|
-
/>
|
|
963
|
-
</Stack>
|
|
964
|
-
</Box>
|
|
965
|
-
|
|
966
|
-
{/* Effects with theme colors */}
|
|
967
|
-
<Box>
|
|
968
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
969
|
-
Effects with Theme Colors
|
|
970
|
-
</Typography>
|
|
971
|
-
<Stack spacing={2}>
|
|
972
|
-
<Progress
|
|
973
|
-
variant="gradient"
|
|
974
|
-
color="primary"
|
|
975
|
-
value={65}
|
|
976
|
-
showLabel
|
|
977
|
-
glow
|
|
978
|
-
data-testid="gradient-glow"
|
|
979
|
-
/>
|
|
980
|
-
<Progress color="success" value={85} showLabel pulse data-testid="linear-pulse" />
|
|
981
|
-
</Stack>
|
|
982
|
-
</Box>
|
|
983
|
-
</Stack>
|
|
984
|
-
</Box>
|
|
985
|
-
),
|
|
986
|
-
play: async ({ canvasElement, step }) => {
|
|
987
|
-
const canvas = within(canvasElement);
|
|
988
|
-
|
|
989
|
-
await step('Theme color application', async () => {
|
|
990
|
-
const primaryProgress = canvas.getByTestId('theme-primary');
|
|
991
|
-
const successProgress = canvas.getByTestId('theme-success');
|
|
992
|
-
const errorProgress = canvas.getByTestId('theme-error');
|
|
993
|
-
|
|
994
|
-
await expect(primaryProgress).toBeInTheDocument();
|
|
995
|
-
await expect(successProgress).toBeInTheDocument();
|
|
996
|
-
await expect(errorProgress).toBeInTheDocument();
|
|
997
|
-
|
|
998
|
-
// Each should be visually distinct (we can't easily test exact colors,
|
|
999
|
-
// but we can verify they render and have different styling)
|
|
1000
|
-
const primaryBar = primaryProgress.querySelector('.MuiLinearProgress-bar');
|
|
1001
|
-
const successBar = successProgress.querySelector('.MuiLinearProgress-bar');
|
|
1002
|
-
|
|
1003
|
-
expect(primaryBar).toBeTruthy();
|
|
1004
|
-
expect(successBar).toBeTruthy();
|
|
1005
|
-
|
|
1006
|
-
if (primaryBar && successBar) {
|
|
1007
|
-
const primaryStyle = window.getComputedStyle(primaryBar);
|
|
1008
|
-
const successStyle = window.getComputedStyle(successBar);
|
|
1009
|
-
|
|
1010
|
-
// Colors should be different
|
|
1011
|
-
expect(primaryStyle.backgroundColor).not.toBe(successStyle.backgroundColor);
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
|
-
|
|
1015
|
-
await step('Circular theme variations', async () => {
|
|
1016
|
-
const circularPrimary = canvas.getByTestId('circular-primary');
|
|
1017
|
-
const circularSuccess = canvas.getByTestId('circular-success');
|
|
1018
|
-
const circularWarning = canvas.getByTestId('circular-warning');
|
|
1019
|
-
|
|
1020
|
-
await expect(circularPrimary).toBeVisible();
|
|
1021
|
-
await expect(circularSuccess).toBeVisible();
|
|
1022
|
-
await expect(circularWarning).toBeVisible();
|
|
1023
|
-
|
|
1024
|
-
// Check for color attributes or styling differences
|
|
1025
|
-
const primaryCircle = circularPrimary.querySelector('.MuiCircularProgress-circle');
|
|
1026
|
-
const successCircle = circularSuccess.querySelector('.MuiCircularProgress-circle');
|
|
1027
|
-
|
|
1028
|
-
if (primaryCircle && successCircle) {
|
|
1029
|
-
const primaryStyle = window.getComputedStyle(primaryCircle);
|
|
1030
|
-
const successStyle = window.getComputedStyle(successCircle);
|
|
1031
|
-
|
|
1032
|
-
// Should have different stroke colors
|
|
1033
|
-
expect(primaryStyle.stroke || primaryStyle.color).not.toBe(
|
|
1034
|
-
successStyle.stroke || successStyle.color,
|
|
1035
|
-
);
|
|
1036
|
-
}
|
|
1037
|
-
});
|
|
1038
|
-
|
|
1039
|
-
await step('Effect styling with theme colors', async () => {
|
|
1040
|
-
const glowProgress = canvas.getByTestId('gradient-glow');
|
|
1041
|
-
const pulseProgress = canvas.getByTestId('linear-pulse');
|
|
1042
|
-
|
|
1043
|
-
await expect(glowProgress).toBeVisible();
|
|
1044
|
-
await expect(pulseProgress).toBeVisible();
|
|
1045
|
-
|
|
1046
|
-
// Verify glow effect is applied
|
|
1047
|
-
const glowBar = glowProgress.querySelector('.MuiLinearProgress-bar');
|
|
1048
|
-
if (glowBar) {
|
|
1049
|
-
const glowStyle = window.getComputedStyle(glowBar);
|
|
1050
|
-
// Should have box-shadow for glow effect
|
|
1051
|
-
expect(glowStyle.boxShadow).not.toBe('none');
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
// Verify pulse animation is applied
|
|
1055
|
-
const pulseBar = pulseProgress.querySelector('.MuiLinearProgress-bar');
|
|
1056
|
-
if (pulseBar) {
|
|
1057
|
-
const pulseStyle = window.getComputedStyle(pulseBar);
|
|
1058
|
-
// Should have animation applied
|
|
1059
|
-
expect(pulseStyle.animation).not.toBe('none');
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
|
|
1063
|
-
await step('Color contrast verification', async () => {
|
|
1064
|
-
// Verify all theme colors render with sufficient contrast
|
|
1065
|
-
const allThemeProgress = [
|
|
1066
|
-
canvas.getByTestId('theme-primary'),
|
|
1067
|
-
canvas.getByTestId('theme-secondary'),
|
|
1068
|
-
canvas.getByTestId('theme-success'),
|
|
1069
|
-
canvas.getByTestId('theme-warning'),
|
|
1070
|
-
canvas.getByTestId('theme-error'),
|
|
1071
|
-
canvas.getByTestId('theme-neutral'),
|
|
1072
|
-
];
|
|
1073
|
-
|
|
1074
|
-
allThemeProgress.forEach(async (progress) => {
|
|
1075
|
-
await expect(progress).toBeVisible();
|
|
1076
|
-
|
|
1077
|
-
const progressBar = progress.querySelector('.MuiLinearProgress-bar');
|
|
1078
|
-
if (progressBar) {
|
|
1079
|
-
const computedStyle = window.getComputedStyle(progressBar);
|
|
1080
|
-
// Should have a background color set
|
|
1081
|
-
expect(computedStyle.backgroundColor).not.toBe('transparent');
|
|
1082
|
-
expect(computedStyle.backgroundColor).not.toBe('');
|
|
1083
|
-
}
|
|
1084
|
-
});
|
|
1085
|
-
});
|
|
1086
|
-
},
|
|
1087
|
-
};
|
|
1088
|
-
|
|
1089
|
-
// Visual States Test
|
|
1090
|
-
export const VisualStates: Story = {
|
|
1091
|
-
name: '👁️ Visual States Test',
|
|
1092
|
-
render: () => (
|
|
1093
|
-
<Box sx={{ p: 3, width: 700 }}>
|
|
1094
|
-
<Typography variant="h6" sx={{ mb: 3 }}>
|
|
1095
|
-
Visual States and Variants
|
|
1096
|
-
</Typography>
|
|
1097
|
-
|
|
1098
|
-
<Stack spacing={4} data-testid="visual-states-container">
|
|
1099
|
-
{/* Default states */}
|
|
1100
|
-
<Box>
|
|
1101
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1102
|
-
Default States
|
|
1103
|
-
</Typography>
|
|
1104
|
-
<Stack spacing={2}>
|
|
1105
|
-
<Box>
|
|
1106
|
-
<Typography variant="caption">Linear - 50%</Typography>
|
|
1107
|
-
<Progress value={50} showLabel data-testid="default-linear" />
|
|
1108
|
-
</Box>
|
|
1109
|
-
<Box>
|
|
1110
|
-
<Typography variant="caption">Indeterminate Linear</Typography>
|
|
1111
|
-
<Progress data-testid="default-indeterminate" />
|
|
1112
|
-
</Box>
|
|
1113
|
-
</Stack>
|
|
1114
|
-
</Box>
|
|
1115
|
-
|
|
1116
|
-
{/* Hover-like states (simulated through styling) */}
|
|
1117
|
-
<Box>
|
|
1118
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1119
|
-
Interactive States
|
|
1120
|
-
</Typography>
|
|
1121
|
-
<Stack spacing={2}>
|
|
1122
|
-
<Box>
|
|
1123
|
-
<Typography variant="caption">With Glow Effect</Typography>
|
|
1124
|
-
<Progress value={75} showLabel glow data-testid="glow-state" />
|
|
1125
|
-
</Box>
|
|
1126
|
-
<Box>
|
|
1127
|
-
<Typography variant="caption">With Pulse Animation</Typography>
|
|
1128
|
-
<Progress value={60} showLabel pulse data-testid="pulse-state" />
|
|
1129
|
-
</Box>
|
|
1130
|
-
<Box>
|
|
1131
|
-
<Typography variant="caption">Glass Effect</Typography>
|
|
1132
|
-
<Progress variant="glass" value={85} showLabel data-testid="glass-state" />
|
|
1133
|
-
</Box>
|
|
1134
|
-
</Stack>
|
|
1135
|
-
</Box>
|
|
1136
|
-
|
|
1137
|
-
{/* Loading states */}
|
|
1138
|
-
<Box>
|
|
1139
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1140
|
-
Loading States
|
|
1141
|
-
</Typography>
|
|
1142
|
-
<Stack spacing={2}>
|
|
1143
|
-
<Box>
|
|
1144
|
-
<Typography variant="caption">Linear Loading</Typography>
|
|
1145
|
-
<Progress data-testid="loading-linear" color="primary" />
|
|
1146
|
-
</Box>
|
|
1147
|
-
<Box>
|
|
1148
|
-
<Typography variant="caption">Circular Loading</Typography>
|
|
1149
|
-
<Progress variant="circular" data-testid="loading-circular" color="primary" />
|
|
1150
|
-
</Box>
|
|
1151
|
-
</Stack>
|
|
1152
|
-
</Box>
|
|
1153
|
-
|
|
1154
|
-
{/* Size variations */}
|
|
1155
|
-
<Box>
|
|
1156
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1157
|
-
Size Variations
|
|
1158
|
-
</Typography>
|
|
1159
|
-
<Stack spacing={2}>
|
|
1160
|
-
<Box>
|
|
1161
|
-
<Typography variant="caption">Small (4px height)</Typography>
|
|
1162
|
-
<Progress size="sm" value={40} showLabel data-testid="small-size" />
|
|
1163
|
-
</Box>
|
|
1164
|
-
<Box>
|
|
1165
|
-
<Typography variant="caption">Medium (6px height)</Typography>
|
|
1166
|
-
<Progress size="md" value={60} showLabel data-testid="medium-size" />
|
|
1167
|
-
</Box>
|
|
1168
|
-
<Box>
|
|
1169
|
-
<Typography variant="caption">Large (8px height)</Typography>
|
|
1170
|
-
<Progress size="lg" value={80} showLabel data-testid="large-size" />
|
|
1171
|
-
</Box>
|
|
1172
|
-
</Stack>
|
|
1173
|
-
</Box>
|
|
1174
|
-
|
|
1175
|
-
{/* Error-like states (using error color) */}
|
|
1176
|
-
<Box>
|
|
1177
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1178
|
-
Status States
|
|
1179
|
-
</Typography>
|
|
1180
|
-
<Stack spacing={2}>
|
|
1181
|
-
<Box>
|
|
1182
|
-
<Typography variant="caption">Success State</Typography>
|
|
1183
|
-
<Progress value={100} showLabel color="success" data-testid="success-state" />
|
|
1184
|
-
</Box>
|
|
1185
|
-
<Box>
|
|
1186
|
-
<Typography variant="caption">Warning State</Typography>
|
|
1187
|
-
<Progress value={25} showLabel color="warning" data-testid="warning-state" />
|
|
1188
|
-
</Box>
|
|
1189
|
-
<Box>
|
|
1190
|
-
<Typography variant="caption">Error State</Typography>
|
|
1191
|
-
<Progress value={15} showLabel color="error" pulse data-testid="error-state" />
|
|
1192
|
-
</Box>
|
|
1193
|
-
</Stack>
|
|
1194
|
-
</Box>
|
|
1195
|
-
</Stack>
|
|
1196
|
-
</Box>
|
|
1197
|
-
),
|
|
1198
|
-
play: async ({ canvasElement, step }) => {
|
|
1199
|
-
const canvas = within(canvasElement);
|
|
1200
|
-
|
|
1201
|
-
await step('Default state rendering', async () => {
|
|
1202
|
-
const defaultLinear = canvas.getByTestId('default-linear');
|
|
1203
|
-
const defaultIndeterminate = canvas.getByTestId('default-indeterminate');
|
|
1204
|
-
|
|
1205
|
-
await expect(defaultLinear).toBeVisible();
|
|
1206
|
-
await expect(defaultIndeterminate).toBeVisible();
|
|
1207
|
-
|
|
1208
|
-
// Check progress value is displayed
|
|
1209
|
-
const label = canvas.getByText('50%');
|
|
1210
|
-
await expect(label).toBeInTheDocument();
|
|
1211
|
-
|
|
1212
|
-
// Verify default styling
|
|
1213
|
-
const linearBar = defaultLinear.querySelector('.MuiLinearProgress-bar');
|
|
1214
|
-
expect(linearBar).toBeTruthy();
|
|
1215
|
-
|
|
1216
|
-
if (linearBar) {
|
|
1217
|
-
const computedStyle = window.getComputedStyle(linearBar);
|
|
1218
|
-
expect(computedStyle.opacity).toBe('1');
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
|
|
1222
|
-
await step('Interactive state effects', async () => {
|
|
1223
|
-
const glowProgress = canvas.getByTestId('glow-state');
|
|
1224
|
-
const pulseProgress = canvas.getByTestId('pulse-state');
|
|
1225
|
-
const glassProgress = canvas.getByTestId('glass-state');
|
|
1226
|
-
|
|
1227
|
-
await expect(glowProgress).toBeVisible();
|
|
1228
|
-
await expect(pulseProgress).toBeVisible();
|
|
1229
|
-
await expect(glassProgress).toBeVisible();
|
|
1230
|
-
|
|
1231
|
-
// Verify glow effect
|
|
1232
|
-
const glowBar = glowProgress.querySelector('.MuiLinearProgress-bar');
|
|
1233
|
-
if (glowBar) {
|
|
1234
|
-
const glowStyle = window.getComputedStyle(glowBar);
|
|
1235
|
-
expect(glowStyle.boxShadow).not.toBe('none');
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
// Verify pulse animation
|
|
1239
|
-
const pulseBar = pulseProgress.querySelector('.MuiLinearProgress-bar');
|
|
1240
|
-
if (pulseBar) {
|
|
1241
|
-
const pulseStyle = window.getComputedStyle(pulseBar);
|
|
1242
|
-
expect(pulseStyle.animation).not.toBe('none');
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
|
-
await step('Loading state behavior', async () => {
|
|
1247
|
-
const loadingLinear = canvas.getByTestId('loading-linear');
|
|
1248
|
-
const loadingCircular = canvas.getByTestId('loading-circular');
|
|
1249
|
-
|
|
1250
|
-
await expect(loadingLinear).toBeVisible();
|
|
1251
|
-
await expect(loadingCircular).toBeVisible();
|
|
1252
|
-
|
|
1253
|
-
// Indeterminate progress should be animated
|
|
1254
|
-
const linearBar1 = loadingLinear.querySelector('.MuiLinearProgress-bar1Indeterminate');
|
|
1255
|
-
const linearBar2 = loadingLinear.querySelector('.MuiLinearProgress-bar2Indeterminate');
|
|
1256
|
-
const circularSvg = loadingCircular.querySelector('.MuiCircularProgress-svg');
|
|
1257
|
-
const circularCircle = loadingCircular.querySelector('.MuiCircularProgress-circle');
|
|
1258
|
-
|
|
1259
|
-
// Linear indeterminate should have animated bars
|
|
1260
|
-
if (linearBar1) {
|
|
1261
|
-
const bar1Style = window.getComputedStyle(linearBar1);
|
|
1262
|
-
expect(bar1Style.animation).not.toBe('none');
|
|
1263
|
-
// MUI uses animation names that may vary by version
|
|
1264
|
-
expect(bar1Style.animation).toBeTruthy();
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
if (linearBar2) {
|
|
1268
|
-
const bar2Style = window.getComputedStyle(linearBar2);
|
|
1269
|
-
expect(bar2Style.animation).not.toBe('none');
|
|
1270
|
-
expect(bar2Style.animation).toBeTruthy();
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// Circular indeterminate should have animation
|
|
1274
|
-
if (circularSvg) {
|
|
1275
|
-
const svgStyle = window.getComputedStyle(circularSvg);
|
|
1276
|
-
// May have animation or use a different approach
|
|
1277
|
-
if (svgStyle.animation && svgStyle.animation !== 'none') {
|
|
1278
|
-
expect(svgStyle.animation).toBeTruthy();
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
if (circularCircle) {
|
|
1283
|
-
const circleStyle = window.getComputedStyle(circularCircle);
|
|
1284
|
-
// Circle should have some animation or transition
|
|
1285
|
-
if (circleStyle.animation && circleStyle.animation !== 'none') {
|
|
1286
|
-
expect(circleStyle.animation).toBeTruthy();
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
await step('Size variation verification', async () => {
|
|
1292
|
-
const smallProgress = canvas.getByTestId('small-size');
|
|
1293
|
-
const mediumProgress = canvas.getByTestId('medium-size');
|
|
1294
|
-
const largeProgress = canvas.getByTestId('large-size');
|
|
1295
|
-
|
|
1296
|
-
await expect(smallProgress).toBeVisible();
|
|
1297
|
-
await expect(mediumProgress).toBeVisible();
|
|
1298
|
-
await expect(largeProgress).toBeVisible();
|
|
1299
|
-
|
|
1300
|
-
// Verify different heights
|
|
1301
|
-
const smallBounds = smallProgress.getBoundingClientRect();
|
|
1302
|
-
const mediumBounds = mediumProgress.getBoundingClientRect();
|
|
1303
|
-
const largeBounds = largeProgress.getBoundingClientRect();
|
|
1304
|
-
|
|
1305
|
-
expect(smallBounds.height).toBeLessThan(mediumBounds.height);
|
|
1306
|
-
expect(mediumBounds.height).toBeLessThan(largeBounds.height);
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
await step('Status state colors', async () => {
|
|
1310
|
-
const successState = canvas.getByTestId('success-state');
|
|
1311
|
-
const warningState = canvas.getByTestId('warning-state');
|
|
1312
|
-
const errorState = canvas.getByTestId('error-state');
|
|
1313
|
-
|
|
1314
|
-
await expect(successState).toBeVisible();
|
|
1315
|
-
await expect(warningState).toBeVisible();
|
|
1316
|
-
await expect(errorState).toBeVisible();
|
|
1317
|
-
|
|
1318
|
-
// Verify different colors are applied
|
|
1319
|
-
const successBar = successState.querySelector('.MuiLinearProgress-bar');
|
|
1320
|
-
const warningBar = warningState.querySelector('.MuiLinearProgress-bar');
|
|
1321
|
-
const errorBar = errorState.querySelector('.MuiLinearProgress-bar');
|
|
1322
|
-
|
|
1323
|
-
if (successBar && warningBar && errorBar) {
|
|
1324
|
-
const successStyle = window.getComputedStyle(successBar);
|
|
1325
|
-
const warningStyle = window.getComputedStyle(warningBar);
|
|
1326
|
-
const errorStyle = window.getComputedStyle(errorBar);
|
|
1327
|
-
|
|
1328
|
-
// Each should have different background colors
|
|
1329
|
-
expect(successStyle.backgroundColor).not.toBe(warningStyle.backgroundColor);
|
|
1330
|
-
expect(warningStyle.backgroundColor).not.toBe(errorStyle.backgroundColor);
|
|
1331
|
-
expect(successStyle.backgroundColor).not.toBe(errorStyle.backgroundColor);
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
// Error state should have pulse animation
|
|
1335
|
-
if (errorBar) {
|
|
1336
|
-
const errorStyle = window.getComputedStyle(errorBar);
|
|
1337
|
-
expect(errorStyle.animation).not.toBe('none');
|
|
1338
|
-
}
|
|
1339
|
-
});
|
|
1340
|
-
},
|
|
1341
|
-
};
|
|
1342
|
-
|
|
1343
|
-
// Performance Test
|
|
1344
|
-
export const Performance: Story = {
|
|
1345
|
-
name: '⚡ Performance Test',
|
|
1346
|
-
render: () => <PerformanceTestComponent />,
|
|
1347
|
-
play: async ({ canvasElement, step }) => {
|
|
1348
|
-
const canvas = within(canvasElement);
|
|
1349
|
-
|
|
1350
|
-
await step('Measure render time', async () => {
|
|
1351
|
-
const startTime = window.performance.now();
|
|
1352
|
-
|
|
1353
|
-
const container = canvas.getByTestId('performance-container');
|
|
1354
|
-
await expect(container).toBeInTheDocument();
|
|
1355
|
-
|
|
1356
|
-
// Find all progress components
|
|
1357
|
-
const progressComponents = container.querySelectorAll('[data-testid^="perf-item-"]');
|
|
1358
|
-
|
|
1359
|
-
const endTime = window.performance.now();
|
|
1360
|
-
const renderTime = endTime - startTime;
|
|
1361
|
-
|
|
1362
|
-
// console.log(`Render time for ${progressComponents.length} progress components: ${renderTime}ms`);
|
|
1363
|
-
|
|
1364
|
-
// Should render 100 components
|
|
1365
|
-
expect(progressComponents.length).toBe(100);
|
|
1366
|
-
|
|
1367
|
-
// Should render in reasonable time (less than 1 second)
|
|
1368
|
-
expect(renderTime).toBeLessThan(1000);
|
|
1369
|
-
});
|
|
1370
|
-
|
|
1371
|
-
await step('Verify all components are visible', async () => {
|
|
1372
|
-
// Test first 10 components for visibility
|
|
1373
|
-
for (let i = 0; i < 10; i++) {
|
|
1374
|
-
const component = canvas.getByTestId(`perf-item-${i}`);
|
|
1375
|
-
await expect(component).toBeVisible();
|
|
1376
|
-
|
|
1377
|
-
// Each should have a progress bar
|
|
1378
|
-
const progressBar = component.querySelector('.MuiLinearProgress-bar');
|
|
1379
|
-
expect(progressBar).toBeTruthy();
|
|
1380
|
-
}
|
|
1381
|
-
});
|
|
1382
|
-
|
|
1383
|
-
await step('Test scroll performance', async () => {
|
|
1384
|
-
const container = canvas.getByTestId('performance-container');
|
|
1385
|
-
const parentContainer = container.closest('[style*="overflow"]') || container.parentElement;
|
|
1386
|
-
|
|
1387
|
-
if (parentContainer) {
|
|
1388
|
-
// Simulate scrolling
|
|
1389
|
-
const scrollStartTime = window.performance.now();
|
|
1390
|
-
|
|
1391
|
-
for (let i = 0; i < 5; i++) {
|
|
1392
|
-
parentContainer.scrollTop = i * 50;
|
|
1393
|
-
await new Promise((resolve) => window.setTimeout(resolve, 10));
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
const scrollEndTime = window.performance.now();
|
|
1397
|
-
const scrollTime = scrollEndTime - scrollStartTime;
|
|
1398
|
-
|
|
1399
|
-
// console.log(`Scroll performance test completed in: ${scrollTime}ms`);
|
|
1400
|
-
|
|
1401
|
-
// Scrolling should be smooth (less than 600ms total for 5 scroll steps with 100 components)
|
|
1402
|
-
// Note: Performance varies depending on system load, so we use a reasonable threshold
|
|
1403
|
-
expect(scrollTime).toBeLessThan(600);
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
// Container should still be present after scrolling
|
|
1407
|
-
await expect(container).toBeInTheDocument();
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
await step('Memory usage consideration', async () => {
|
|
1411
|
-
// Verify that components are properly rendered without memory leaks
|
|
1412
|
-
const allProgressBars = canvas.getAllByTestId(/^perf-item-/);
|
|
1413
|
-
|
|
1414
|
-
// All 100 components should be found
|
|
1415
|
-
expect(allProgressBars.length).toBe(100);
|
|
1416
|
-
|
|
1417
|
-
// Each should be a valid DOM element
|
|
1418
|
-
allProgressBars.forEach((bar, index) => {
|
|
1419
|
-
expect(bar).toBeInTheDocument();
|
|
1420
|
-
expect(bar).toHaveAttribute('data-testid', `perf-item-${index}`);
|
|
1421
|
-
});
|
|
1422
|
-
});
|
|
1423
|
-
|
|
1424
|
-
// Add a final PASS indicator for the test
|
|
1425
|
-
await step('Performance test completed', async () => {
|
|
1426
|
-
const statusElement = document.createElement('div');
|
|
1427
|
-
statusElement.setAttribute('aria-label', 'Status of the test run');
|
|
1428
|
-
statusElement.textContent = 'PASS';
|
|
1429
|
-
document.body.appendChild(statusElement);
|
|
1430
|
-
});
|
|
1431
|
-
},
|
|
1432
|
-
};
|
|
1433
|
-
|
|
1434
|
-
const PerformanceTestComponent = () => {
|
|
1435
|
-
const [items] = React.useState(() =>
|
|
1436
|
-
Array.from({ length: 100 }, (_, i) => ({
|
|
1437
|
-
id: i,
|
|
1438
|
-
value: Math.floor(Math.random() * 100),
|
|
1439
|
-
label: `Item ${i + 1}`,
|
|
1440
|
-
})),
|
|
1441
|
-
);
|
|
1442
|
-
|
|
1443
|
-
return (
|
|
1444
|
-
<Box sx={{ p: 3, maxHeight: 400, overflow: 'auto' }}>
|
|
1445
|
-
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
1446
|
-
Performance Test - 100 Progress Bars
|
|
1447
|
-
</Typography>
|
|
1448
|
-
|
|
1449
|
-
<Box data-testid="performance-container">
|
|
1450
|
-
<Stack spacing={1}>
|
|
1451
|
-
{items.map((item, index) => (
|
|
1452
|
-
<Box key={item.id} sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
1453
|
-
<Typography variant="caption" sx={{ minWidth: 60 }}>
|
|
1454
|
-
{item.label}
|
|
1455
|
-
</Typography>
|
|
1456
|
-
<Progress
|
|
1457
|
-
value={item.value}
|
|
1458
|
-
showLabel
|
|
1459
|
-
size="sm"
|
|
1460
|
-
data-testid={`perf-item-${index}`}
|
|
1461
|
-
sx={{ flex: 1 }}
|
|
1462
|
-
/>
|
|
1463
|
-
</Box>
|
|
1464
|
-
))}
|
|
1465
|
-
</Stack>
|
|
1466
|
-
</Box>
|
|
1467
|
-
|
|
1468
|
-
<Typography variant="caption" sx={{ mt: 2, display: 'block' }}>
|
|
1469
|
-
Rendered {items.length} progress components
|
|
1470
|
-
</Typography>
|
|
1471
|
-
</Box>
|
|
1472
|
-
);
|
|
1473
|
-
};
|
|
1474
|
-
|
|
1475
|
-
// Edge Cases Test
|
|
1476
|
-
export const EdgeCases: Story = {
|
|
1477
|
-
name: '🔧 Edge Cases Test',
|
|
1478
|
-
render: () => (
|
|
1479
|
-
<Box sx={{ p: 3, width: 600 }}>
|
|
1480
|
-
<Typography variant="h6" sx={{ mb: 3 }}>
|
|
1481
|
-
Edge Cases and Boundary Conditions
|
|
1482
|
-
</Typography>
|
|
1483
|
-
|
|
1484
|
-
<Stack spacing={4} data-testid="edge-cases-container">
|
|
1485
|
-
{/* Boundary values */}
|
|
1486
|
-
<Box>
|
|
1487
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1488
|
-
Boundary Values
|
|
1489
|
-
</Typography>
|
|
1490
|
-
<Stack spacing={2}>
|
|
1491
|
-
<Box>
|
|
1492
|
-
<Typography variant="caption">0% Progress</Typography>
|
|
1493
|
-
<Progress value={0} showLabel data-testid="zero-progress" />
|
|
1494
|
-
</Box>
|
|
1495
|
-
<Box>
|
|
1496
|
-
<Typography variant="caption">100% Progress</Typography>
|
|
1497
|
-
<Progress value={100} showLabel data-testid="full-progress" />
|
|
1498
|
-
</Box>
|
|
1499
|
-
<Box>
|
|
1500
|
-
<Typography variant="caption">Over 100% (should cap at 100%)</Typography>
|
|
1501
|
-
<Progress value={150} showLabel data-testid="over-progress" />
|
|
1502
|
-
</Box>
|
|
1503
|
-
<Box>
|
|
1504
|
-
<Typography variant="caption">Negative Value (should default to 0)</Typography>
|
|
1505
|
-
<Progress value={-25} showLabel data-testid="negative-progress" />
|
|
1506
|
-
</Box>
|
|
1507
|
-
</Stack>
|
|
1508
|
-
</Box>
|
|
1509
|
-
|
|
1510
|
-
{/* Invalid or undefined values */}
|
|
1511
|
-
<Box>
|
|
1512
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1513
|
-
Invalid/Undefined Values
|
|
1514
|
-
</Typography>
|
|
1515
|
-
<Stack spacing={2}>
|
|
1516
|
-
<Box>
|
|
1517
|
-
<Typography variant="caption">Undefined Value (Indeterminate)</Typography>
|
|
1518
|
-
<Progress value={undefined} showLabel data-testid="undefined-progress" />
|
|
1519
|
-
</Box>
|
|
1520
|
-
<Box>
|
|
1521
|
-
<Typography variant="caption">NaN Value</Typography>
|
|
1522
|
-
<Progress value={NaN} showLabel data-testid="nan-progress" />
|
|
1523
|
-
</Box>
|
|
1524
|
-
<Box>
|
|
1525
|
-
<Typography variant="caption">Float Value (75.5%)</Typography>
|
|
1526
|
-
<Progress value={75.5} showLabel data-testid="float-progress" />
|
|
1527
|
-
</Box>
|
|
1528
|
-
</Stack>
|
|
1529
|
-
</Box>
|
|
1530
|
-
|
|
1531
|
-
{/* Extreme configurations */}
|
|
1532
|
-
<Box>
|
|
1533
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1534
|
-
Extreme Configurations
|
|
1535
|
-
</Typography>
|
|
1536
|
-
<Stack spacing={2}>
|
|
1537
|
-
<Box>
|
|
1538
|
-
<Typography variant="caption">Very Long Label</Typography>
|
|
1539
|
-
<Progress
|
|
1540
|
-
value={50}
|
|
1541
|
-
label="This is a very long label that might cause overflow issues in the progress component layout and should be handled gracefully"
|
|
1542
|
-
showLabel
|
|
1543
|
-
data-testid="long-label-progress"
|
|
1544
|
-
/>
|
|
1545
|
-
</Box>
|
|
1546
|
-
<Box>
|
|
1547
|
-
<Typography variant="caption">Empty Label</Typography>
|
|
1548
|
-
<Progress value={75} label="" showLabel data-testid="empty-label-progress" />
|
|
1549
|
-
</Box>
|
|
1550
|
-
<Box>
|
|
1551
|
-
<Typography variant="caption">Many Segments (50)</Typography>
|
|
1552
|
-
<Progress
|
|
1553
|
-
variant="segmented"
|
|
1554
|
-
segments={50}
|
|
1555
|
-
value={60}
|
|
1556
|
-
showLabel
|
|
1557
|
-
data-testid="many-segments-progress"
|
|
1558
|
-
sx={{ maxWidth: '100%' }}
|
|
1559
|
-
/>
|
|
1560
|
-
</Box>
|
|
1561
|
-
<Box>
|
|
1562
|
-
<Typography variant="caption">Single Segment</Typography>
|
|
1563
|
-
<Progress
|
|
1564
|
-
variant="segmented"
|
|
1565
|
-
segments={1}
|
|
1566
|
-
value={80}
|
|
1567
|
-
showLabel
|
|
1568
|
-
data-testid="single-segment-progress"
|
|
1569
|
-
/>
|
|
1570
|
-
</Box>
|
|
1571
|
-
</Stack>
|
|
1572
|
-
</Box>
|
|
1573
|
-
|
|
1574
|
-
{/* Rapid updates simulation */}
|
|
1575
|
-
<Box>
|
|
1576
|
-
<Typography variant="subtitle2" sx={{ mb: 2 }}>
|
|
1577
|
-
Rapid Updates
|
|
1578
|
-
</Typography>
|
|
1579
|
-
<RapidUpdateProgress />
|
|
1580
|
-
</Box>
|
|
1581
|
-
</Stack>
|
|
1582
|
-
</Box>
|
|
1583
|
-
),
|
|
1584
|
-
play: async ({ canvasElement, step }) => {
|
|
1585
|
-
const canvas = within(canvasElement);
|
|
1586
|
-
|
|
1587
|
-
await step('Boundary value handling', async () => {
|
|
1588
|
-
const zeroProgress = canvas.getByTestId('zero-progress');
|
|
1589
|
-
const fullProgress = canvas.getByTestId('full-progress');
|
|
1590
|
-
const overProgress = canvas.getByTestId('over-progress');
|
|
1591
|
-
const negativeProgress = canvas.getByTestId('negative-progress');
|
|
1592
|
-
|
|
1593
|
-
await expect(zeroProgress).toBeVisible();
|
|
1594
|
-
await expect(fullProgress).toBeVisible();
|
|
1595
|
-
await expect(overProgress).toBeVisible();
|
|
1596
|
-
await expect(negativeProgress).toBeVisible();
|
|
1597
|
-
|
|
1598
|
-
// Check labels show correct values using testid containers
|
|
1599
|
-
const zeroProgressContainer = canvas.getByTestId('zero-progress').closest('div');
|
|
1600
|
-
const fullProgressContainer = canvas.getByTestId('full-progress').closest('div');
|
|
1601
|
-
const overProgressContainer = canvas.getByTestId('over-progress').closest('div');
|
|
1602
|
-
const negativeProgressContainer = canvas.getByTestId('negative-progress').closest('div');
|
|
1603
|
-
|
|
1604
|
-
// Verify the components contain the expected labels
|
|
1605
|
-
await expect(zeroProgressContainer).toHaveTextContent('0%');
|
|
1606
|
-
await expect(fullProgressContainer).toHaveTextContent('100%');
|
|
1607
|
-
// Over 100% should be capped at 100% (MUI behavior) or show actual value
|
|
1608
|
-
const overText = overProgressContainer?.textContent || '';
|
|
1609
|
-
expect(overText).toMatch(/100%|150%/); // Could be capped or show actual
|
|
1610
|
-
// Negative should show 0% (clamped) or actual negative value
|
|
1611
|
-
const negativeText = negativeProgressContainer?.textContent || '';
|
|
1612
|
-
expect(negativeText).toMatch(/0%|-25%/);
|
|
1613
|
-
|
|
1614
|
-
// Verify actual progress bar exists for boundary values
|
|
1615
|
-
const zeroBar = zeroProgress.querySelector('.MuiLinearProgress-bar');
|
|
1616
|
-
const fullBar = fullProgress.querySelector('.MuiLinearProgress-bar');
|
|
1617
|
-
|
|
1618
|
-
// Just verify bars exist and are rendered
|
|
1619
|
-
if (zeroBar) {
|
|
1620
|
-
expect(zeroBar).toBeInTheDocument();
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
if (fullBar) {
|
|
1624
|
-
expect(fullBar).toBeInTheDocument();
|
|
1625
|
-
}
|
|
1626
|
-
});
|
|
1627
|
-
|
|
1628
|
-
await step('Invalid value handling', async () => {
|
|
1629
|
-
const undefinedProgress = canvas.getByTestId('undefined-progress');
|
|
1630
|
-
const nanProgress = canvas.getByTestId('nan-progress');
|
|
1631
|
-
const floatProgress = canvas.getByTestId('float-progress');
|
|
1632
|
-
|
|
1633
|
-
await expect(undefinedProgress).toBeVisible();
|
|
1634
|
-
await expect(nanProgress).toBeVisible();
|
|
1635
|
-
await expect(floatProgress).toBeVisible();
|
|
1636
|
-
|
|
1637
|
-
// Float value should be handled correctly
|
|
1638
|
-
const floatLabel = canvas.getByText('76%'); // Should round 75.5 to 76
|
|
1639
|
-
await expect(floatLabel).toBeInTheDocument();
|
|
1640
|
-
|
|
1641
|
-
// Verify float progress bar exists
|
|
1642
|
-
const floatBar = floatProgress.querySelector('.MuiLinearProgress-bar');
|
|
1643
|
-
if (floatBar) {
|
|
1644
|
-
// Just verify the bar exists and is rendered
|
|
1645
|
-
expect(floatBar).toBeInTheDocument();
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
// Undefined should work as indeterminate
|
|
1649
|
-
const undefinedBar1 = undefinedProgress.querySelector('.MuiLinearProgress-bar1Indeterminate');
|
|
1650
|
-
const undefinedBar2 = undefinedProgress.querySelector('.MuiLinearProgress-bar2Indeterminate');
|
|
1651
|
-
const undefinedBarDeterminate = undefinedProgress.querySelector('.MuiLinearProgress-bar');
|
|
1652
|
-
|
|
1653
|
-
// Should have indeterminate bars or a regular bar
|
|
1654
|
-
expect(undefinedBar1 || undefinedBar2 || undefinedBarDeterminate).toBeTruthy();
|
|
1655
|
-
|
|
1656
|
-
// If indeterminate, should have animation
|
|
1657
|
-
if (undefinedBar1) {
|
|
1658
|
-
const bar1Style = window.getComputedStyle(undefinedBar1);
|
|
1659
|
-
expect(bar1Style.animation).not.toBe('none');
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
// NaN should be handled gracefully (treated as 0)
|
|
1663
|
-
const nanBar = nanProgress.querySelector('.MuiLinearProgress-bar');
|
|
1664
|
-
expect(nanBar).toBeTruthy();
|
|
1665
|
-
|
|
1666
|
-
if (nanBar) {
|
|
1667
|
-
// Just verify the bar exists and is rendered
|
|
1668
|
-
expect(nanBar).toBeInTheDocument();
|
|
1669
|
-
}
|
|
1670
|
-
});
|
|
1671
|
-
|
|
1672
|
-
await step('Extreme configuration handling', async () => {
|
|
1673
|
-
const longLabelProgress = canvas.getByTestId('long-label-progress');
|
|
1674
|
-
const emptyLabelProgress = canvas.getByTestId('empty-label-progress');
|
|
1675
|
-
const singleSegmentProgress = canvas.getByTestId('single-segment-progress');
|
|
1676
|
-
|
|
1677
|
-
await expect(longLabelProgress).toBeVisible();
|
|
1678
|
-
await expect(emptyLabelProgress).toBeVisible();
|
|
1679
|
-
await expect(singleSegmentProgress).toBeVisible();
|
|
1680
|
-
|
|
1681
|
-
// Test many segments (50 segments at 60% = 30 filled)
|
|
1682
|
-
const manySegmentsProgress = canvas.getByTestId('many-segments-progress');
|
|
1683
|
-
await expect(manySegmentsProgress).toBeVisible();
|
|
1684
|
-
|
|
1685
|
-
// Many segments should render without breaking - look for child boxes (segments)
|
|
1686
|
-
const manySegmentContainer = manySegmentsProgress.querySelector(
|
|
1687
|
-
'div[style*="display: flex"], div div',
|
|
1688
|
-
);
|
|
1689
|
-
if (manySegmentContainer) {
|
|
1690
|
-
const segments = manySegmentContainer.children;
|
|
1691
|
-
expect(segments.length).toBe(50); // Should have exactly 50 segments
|
|
1692
|
-
|
|
1693
|
-
// Count filled segments for 60% progress
|
|
1694
|
-
let filledCount = 0;
|
|
1695
|
-
for (let i = 0; i < segments.length; i++) {
|
|
1696
|
-
const segment = segments[i] as HTMLElement;
|
|
1697
|
-
const style = window.getComputedStyle(segment);
|
|
1698
|
-
const bgColor = style.backgroundColor;
|
|
1699
|
-
|
|
1700
|
-
// Check if segment is filled (not transparent/low opacity)
|
|
1701
|
-
if (bgColor && !bgColor.includes('0.1') && bgColor !== 'rgba(0, 0, 0, 0)') {
|
|
1702
|
-
filledCount++;
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
// For 60% of 50 segments, expect 30 filled
|
|
1707
|
-
expect(filledCount).toBe(30);
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
// Long label should not break layout
|
|
1711
|
-
const longLabelText = longLabelProgress.querySelector('span');
|
|
1712
|
-
if (longLabelText) {
|
|
1713
|
-
const boundingBox = longLabelText.getBoundingClientRect();
|
|
1714
|
-
expect(boundingBox.width).toBeGreaterThan(0);
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// Empty label should still render progress
|
|
1718
|
-
const emptyBar = emptyLabelProgress.querySelector('.MuiLinearProgress-bar');
|
|
1719
|
-
expect(emptyBar).toBeTruthy();
|
|
1720
|
-
|
|
1721
|
-
// Single segment should render as one block
|
|
1722
|
-
const singleSegmentContainer = singleSegmentProgress.querySelector(
|
|
1723
|
-
'div[style*="display: flex"], div div',
|
|
1724
|
-
);
|
|
1725
|
-
if (singleSegmentContainer) {
|
|
1726
|
-
const singleSegments = singleSegmentContainer.children;
|
|
1727
|
-
expect(singleSegments.length).toBe(1);
|
|
1728
|
-
} else {
|
|
1729
|
-
// Just verify the component is visible if we can't find segments
|
|
1730
|
-
await expect(singleSegmentProgress).toBeVisible();
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
|
|
1734
|
-
await step('Component stability', async () => {
|
|
1735
|
-
// All edge case components should be stable and not crash
|
|
1736
|
-
const allProgressComponents = [
|
|
1737
|
-
'zero-progress',
|
|
1738
|
-
'full-progress',
|
|
1739
|
-
'over-progress',
|
|
1740
|
-
'negative-progress',
|
|
1741
|
-
'undefined-progress',
|
|
1742
|
-
'nan-progress',
|
|
1743
|
-
'float-progress',
|
|
1744
|
-
'long-label-progress',
|
|
1745
|
-
'empty-label-progress',
|
|
1746
|
-
'many-segments-progress',
|
|
1747
|
-
'single-segment-progress',
|
|
1748
|
-
];
|
|
1749
|
-
|
|
1750
|
-
allProgressComponents.forEach(async (testId) => {
|
|
1751
|
-
const component = canvas.getByTestId(testId);
|
|
1752
|
-
await expect(component).toBeInTheDocument();
|
|
1753
|
-
await expect(component).toBeVisible();
|
|
1754
|
-
|
|
1755
|
-
// Should not have any error boundaries or broken rendering
|
|
1756
|
-
const hasError = component.querySelector('[data-error], .error, [aria-errormessage]');
|
|
1757
|
-
expect(hasError).toBeFalsy();
|
|
1758
|
-
});
|
|
1759
|
-
});
|
|
1760
|
-
},
|
|
1761
|
-
};
|
|
1762
|
-
|
|
1763
|
-
// Rapid Update Progress Component
|
|
1764
|
-
const RapidUpdateProgress: React.FC = () => {
|
|
1765
|
-
const [progress, setProgress] = React.useState(0);
|
|
1766
|
-
const [isUpdating, setIsUpdating] = React.useState(false);
|
|
1767
|
-
|
|
1768
|
-
const startRapidUpdates = () => {
|
|
1769
|
-
setIsUpdating(true);
|
|
1770
|
-
setProgress(0);
|
|
1771
|
-
|
|
1772
|
-
let currentProgress = 0;
|
|
1773
|
-
const interval = window.setInterval(() => {
|
|
1774
|
-
currentProgress += Math.random() * 5;
|
|
1775
|
-
if (currentProgress >= 100) {
|
|
1776
|
-
currentProgress = 100;
|
|
1777
|
-
window.clearInterval(interval);
|
|
1778
|
-
setIsUpdating(false);
|
|
1779
|
-
}
|
|
1780
|
-
setProgress(currentProgress);
|
|
1781
|
-
}, 50); // Very rapid updates every 50ms
|
|
1782
|
-
};
|
|
1783
|
-
|
|
1784
|
-
return (
|
|
1785
|
-
<Box>
|
|
1786
|
-
<Progress
|
|
1787
|
-
value={Math.round(progress)}
|
|
1788
|
-
showLabel
|
|
1789
|
-
data-testid="rapid-update-progress"
|
|
1790
|
-
pulse={isUpdating}
|
|
1791
|
-
/>
|
|
1792
|
-
<Button
|
|
1793
|
-
variant="outlined"
|
|
1794
|
-
size="small"
|
|
1795
|
-
onClick={startRapidUpdates}
|
|
1796
|
-
disabled={isUpdating}
|
|
1797
|
-
sx={{ mt: 1 }}
|
|
1798
|
-
data-testid="rapid-update-button"
|
|
1799
|
-
>
|
|
1800
|
-
{isUpdating ? 'Updating...' : 'Start Rapid Updates'}
|
|
1801
|
-
</Button>
|
|
1802
|
-
</Box>
|
|
1803
|
-
);
|
|
1804
|
-
};
|
|
1805
|
-
|
|
1806
|
-
// Integration Test
|
|
1807
|
-
const IntegrationTestComponent = () => {
|
|
1808
|
-
const [uploadProgress, setUploadProgress] = React.useState(0);
|
|
1809
|
-
const [processingProgress, setProcessingProgress] = React.useState(0);
|
|
1810
|
-
const [currentStep, setCurrentStep] = React.useState(0);
|
|
1811
|
-
const [isActive, setIsActive] = React.useState(false);
|
|
1812
|
-
|
|
1813
|
-
const steps = ['Initializing', 'Uploading files', 'Processing data', 'Finalizing', 'Complete'];
|
|
1814
|
-
|
|
1815
|
-
const startWorkflow = () => {
|
|
1816
|
-
setIsActive(true);
|
|
1817
|
-
setCurrentStep(0);
|
|
1818
|
-
setUploadProgress(0);
|
|
1819
|
-
setProcessingProgress(0);
|
|
1820
|
-
|
|
1821
|
-
// Step 1: Upload simulation
|
|
1822
|
-
let upload = 0;
|
|
1823
|
-
const uploadInterval = window.setInterval(() => {
|
|
1824
|
-
upload += Math.random() * 15;
|
|
1825
|
-
if (upload >= 100) {
|
|
1826
|
-
upload = 100;
|
|
1827
|
-
setUploadProgress(upload);
|
|
1828
|
-
setCurrentStep(2);
|
|
1829
|
-
window.clearInterval(uploadInterval);
|
|
1830
|
-
|
|
1831
|
-
// Step 2: Processing simulation
|
|
1832
|
-
let processing = 0;
|
|
1833
|
-
const processInterval = window.setInterval(() => {
|
|
1834
|
-
processing += Math.random() * 10;
|
|
1835
|
-
if (processing >= 100) {
|
|
1836
|
-
processing = 100;
|
|
1837
|
-
setProcessingProgress(processing);
|
|
1838
|
-
setCurrentStep(4);
|
|
1839
|
-
setIsActive(false);
|
|
1840
|
-
window.clearInterval(processInterval);
|
|
1841
|
-
} else {
|
|
1842
|
-
setProcessingProgress(processing);
|
|
1843
|
-
}
|
|
1844
|
-
}, 150);
|
|
1845
|
-
} else {
|
|
1846
|
-
setUploadProgress(upload);
|
|
1847
|
-
setCurrentStep(1);
|
|
1848
|
-
}
|
|
1849
|
-
}, 100);
|
|
1850
|
-
};
|
|
1851
|
-
|
|
1852
|
-
return (
|
|
1853
|
-
<Box sx={{ width: 500, p: 3 }}>
|
|
1854
|
-
<Typography variant="h6" sx={{ mb: 3 }}>
|
|
1855
|
-
Multi-Component Integration Test
|
|
1856
|
-
</Typography>
|
|
1857
|
-
|
|
1858
|
-
{/* Step indicator */}
|
|
1859
|
-
<Box sx={{ mb: 3 }} data-testid="step-indicator">
|
|
1860
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
1861
|
-
Current Step: {steps[currentStep]}
|
|
1862
|
-
</Typography>
|
|
1863
|
-
<Progress
|
|
1864
|
-
variant="segmented"
|
|
1865
|
-
segments={5}
|
|
1866
|
-
value={(currentStep / 4) * 100}
|
|
1867
|
-
color="primary"
|
|
1868
|
-
data-testid="step-progress"
|
|
1869
|
-
/>
|
|
1870
|
-
</Box>
|
|
1871
|
-
|
|
1872
|
-
{/* Upload progress */}
|
|
1873
|
-
<Box sx={{ mb: 3 }}>
|
|
1874
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
1875
|
-
Upload Progress
|
|
1876
|
-
</Typography>
|
|
1877
|
-
<Progress
|
|
1878
|
-
value={uploadProgress}
|
|
1879
|
-
showLabel
|
|
1880
|
-
color="success"
|
|
1881
|
-
glow={currentStep === 1}
|
|
1882
|
-
data-testid="upload-progress"
|
|
1883
|
-
/>
|
|
1884
|
-
</Box>
|
|
1885
|
-
|
|
1886
|
-
{/* Processing progress */}
|
|
1887
|
-
<Box sx={{ mb: 3 }}>
|
|
1888
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
1889
|
-
Processing Progress
|
|
1890
|
-
</Typography>
|
|
1891
|
-
<Progress
|
|
1892
|
-
variant="circular"
|
|
1893
|
-
value={processingProgress}
|
|
1894
|
-
showLabel
|
|
1895
|
-
color="warning"
|
|
1896
|
-
pulse={currentStep === 2}
|
|
1897
|
-
data-testid="processing-progress"
|
|
1898
|
-
/>
|
|
1899
|
-
</Box>
|
|
1900
|
-
|
|
1901
|
-
{/* Overall progress */}
|
|
1902
|
-
<Box sx={{ mb: 3 }}>
|
|
1903
|
-
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
1904
|
-
Overall Progress
|
|
1905
|
-
</Typography>
|
|
1906
|
-
<Progress
|
|
1907
|
-
variant="gradient"
|
|
1908
|
-
value={(uploadProgress + processingProgress) / 2}
|
|
1909
|
-
showLabel
|
|
1910
|
-
color="primary"
|
|
1911
|
-
glow={isActive}
|
|
1912
|
-
data-testid="overall-progress"
|
|
1913
|
-
/>
|
|
1914
|
-
</Box>
|
|
1915
|
-
|
|
1916
|
-
{/* Control button */}
|
|
1917
|
-
<Button
|
|
1918
|
-
variant="contained"
|
|
1919
|
-
onClick={startWorkflow}
|
|
1920
|
-
disabled={isActive}
|
|
1921
|
-
data-testid="start-workflow-button"
|
|
1922
|
-
fullWidth
|
|
1923
|
-
>
|
|
1924
|
-
{isActive ? 'Workflow Running...' : 'Start Workflow'}
|
|
1925
|
-
</Button>
|
|
1926
|
-
|
|
1927
|
-
{/* Status message */}
|
|
1928
|
-
<Typography variant="body2" sx={{ mt: 2, textAlign: 'center' }} data-testid="status-message">
|
|
1929
|
-
Status: {isActive ? 'Running' : currentStep === 4 ? 'Completed' : 'Ready'}
|
|
1930
|
-
</Typography>
|
|
1931
|
-
</Box>
|
|
1932
|
-
);
|
|
1933
|
-
};
|
|
1934
|
-
|
|
1935
|
-
export const Integration: Story = {
|
|
1936
|
-
name: '🔗 Integration Test',
|
|
1937
|
-
render: () => <IntegrationTestComponent />,
|
|
1938
|
-
play: async ({ canvasElement, step }) => {
|
|
1939
|
-
const canvas = within(canvasElement);
|
|
1940
|
-
|
|
1941
|
-
await step('Initial integration state', async () => {
|
|
1942
|
-
const stepProgress = canvas.getByTestId('step-progress');
|
|
1943
|
-
const uploadProgress = canvas.getByTestId('upload-progress');
|
|
1944
|
-
const processingProgress = canvas.getByTestId('processing-progress');
|
|
1945
|
-
const overallProgress = canvas.getByTestId('overall-progress');
|
|
1946
|
-
const startButton = canvas.getByTestId('start-workflow-button');
|
|
1947
|
-
|
|
1948
|
-
await expect(stepProgress).toBeInTheDocument();
|
|
1949
|
-
await expect(uploadProgress).toBeInTheDocument();
|
|
1950
|
-
await expect(processingProgress).toBeInTheDocument();
|
|
1951
|
-
await expect(overallProgress).toBeInTheDocument();
|
|
1952
|
-
await expect(startButton).toBeEnabled();
|
|
1953
|
-
|
|
1954
|
-
// Initial status should be "Ready"
|
|
1955
|
-
const statusMessage = canvas.getByText('Status: Ready');
|
|
1956
|
-
await expect(statusMessage).toBeInTheDocument();
|
|
1957
|
-
});
|
|
1958
|
-
|
|
1959
|
-
await step('Start workflow integration', async () => {
|
|
1960
|
-
const startButton = canvas.getByTestId('start-workflow-button');
|
|
1961
|
-
await userEvent.click(startButton);
|
|
1962
|
-
|
|
1963
|
-
// Button should become disabled
|
|
1964
|
-
await expect(startButton).toBeDisabled();
|
|
1965
|
-
|
|
1966
|
-
// Status should change to running
|
|
1967
|
-
await waitFor(() => {
|
|
1968
|
-
const runningStatus = canvas.getByText('Status: Running');
|
|
1969
|
-
expect(runningStatus).toBeInTheDocument();
|
|
1970
|
-
});
|
|
1971
|
-
|
|
1972
|
-
// Step indicator should show progress
|
|
1973
|
-
const stepIndicator = canvas.getByTestId('step-indicator');
|
|
1974
|
-
await expect(stepIndicator).toContainElement(canvas.getByText(/Current Step:/));
|
|
1975
|
-
});
|
|
1976
|
-
|
|
1977
|
-
await step('Upload phase integration', async () => {
|
|
1978
|
-
// Wait for upload to start
|
|
1979
|
-
await waitFor(
|
|
1980
|
-
() => {
|
|
1981
|
-
const uploadProgress = canvas.getByTestId('upload-progress');
|
|
1982
|
-
const progressBar = uploadProgress.querySelector('.MuiLinearProgress-bar');
|
|
1983
|
-
if (progressBar) {
|
|
1984
|
-
// Just verify the progress bar exists and is visible
|
|
1985
|
-
expect(progressBar).toBeVisible();
|
|
1986
|
-
}
|
|
1987
|
-
},
|
|
1988
|
-
{ timeout: 3000 },
|
|
1989
|
-
);
|
|
1990
|
-
|
|
1991
|
-
// Step should show "Uploading files"
|
|
1992
|
-
await waitFor(
|
|
1993
|
-
() => {
|
|
1994
|
-
const uploadingStep = canvas.getByText('Current Step: Uploading files');
|
|
1995
|
-
expect(uploadingStep).toBeInTheDocument();
|
|
1996
|
-
},
|
|
1997
|
-
{ timeout: 2000 },
|
|
1998
|
-
);
|
|
1999
|
-
});
|
|
2000
|
-
|
|
2001
|
-
await step('Processing phase integration', async () => {
|
|
2002
|
-
// Wait for processing to start
|
|
2003
|
-
await waitFor(
|
|
2004
|
-
() => {
|
|
2005
|
-
const processingStep = canvas.getByText('Current Step: Processing data');
|
|
2006
|
-
expect(processingStep).toBeInTheDocument();
|
|
2007
|
-
},
|
|
2008
|
-
{ timeout: 5000 },
|
|
2009
|
-
);
|
|
2010
|
-
|
|
2011
|
-
// Processing progress should have pulse effect
|
|
2012
|
-
const processingProgress = canvas.getByTestId('processing-progress');
|
|
2013
|
-
const circularSvg = processingProgress.querySelector('.MuiCircularProgress-svg');
|
|
2014
|
-
|
|
2015
|
-
if (circularSvg) {
|
|
2016
|
-
const style = window.getComputedStyle(circularSvg);
|
|
2017
|
-
// Should have animation for pulse
|
|
2018
|
-
expect(style.animation).not.toBe('none');
|
|
2019
|
-
}
|
|
2020
|
-
});
|
|
2021
|
-
|
|
2022
|
-
await step('Workflow completion integration', async () => {
|
|
2023
|
-
// Wait for completion
|
|
2024
|
-
await waitFor(
|
|
2025
|
-
() => {
|
|
2026
|
-
const completedStatus = canvas.getByText('Status: Completed');
|
|
2027
|
-
expect(completedStatus).toBeInTheDocument();
|
|
2028
|
-
},
|
|
2029
|
-
{ timeout: 10000 },
|
|
2030
|
-
);
|
|
2031
|
-
|
|
2032
|
-
// Final step should show "Complete"
|
|
2033
|
-
const completeStep = canvas.getByText('Current Step: Complete');
|
|
2034
|
-
await expect(completeStep).toBeInTheDocument();
|
|
2035
|
-
|
|
2036
|
-
// Start button should be re-enabled
|
|
2037
|
-
const startButton = canvas.getByTestId('start-workflow-button');
|
|
2038
|
-
await expect(startButton).toBeEnabled();
|
|
2039
|
-
|
|
2040
|
-
// Overall progress should show completion
|
|
2041
|
-
const overallProgress = canvas.getByTestId('overall-progress');
|
|
2042
|
-
const overallLabel = overallProgress.querySelector('span');
|
|
2043
|
-
if (overallLabel && overallLabel.textContent) {
|
|
2044
|
-
const progressValue = parseInt(overallLabel.textContent);
|
|
2045
|
-
expect(progressValue).toBeGreaterThan(80); // Should be near completion
|
|
2046
|
-
}
|
|
2047
|
-
});
|
|
2048
|
-
|
|
2049
|
-
await step('Component interaction verification', async () => {
|
|
2050
|
-
// All progress components should be present and functional
|
|
2051
|
-
const allProgressComponents = [
|
|
2052
|
-
canvas.getByTestId('step-progress'),
|
|
2053
|
-
canvas.getByTestId('upload-progress'),
|
|
2054
|
-
canvas.getByTestId('processing-progress'),
|
|
2055
|
-
canvas.getByTestId('overall-progress'),
|
|
2056
|
-
];
|
|
2057
|
-
|
|
2058
|
-
allProgressComponents.forEach(async (component) => {
|
|
2059
|
-
await expect(component).toBeVisible();
|
|
2060
|
-
await expect(component).toBeInTheDocument();
|
|
2061
|
-
|
|
2062
|
-
// Each should have some form of progress indication
|
|
2063
|
-
const hasProgressBar = component.querySelector(
|
|
2064
|
-
'.MuiLinearProgress-bar, .MuiCircularProgress-svg, div[style*="display: flex"], div div',
|
|
2065
|
-
);
|
|
2066
|
-
expect(hasProgressBar).toBeTruthy();
|
|
2067
|
-
});
|
|
2068
|
-
});
|
|
2069
|
-
},
|
|
2070
|
-
};
|