@syntrologie/adapt-overlays 2.16.0 → 2.18.0
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/dist/chunk-VHAA22YE.js +14 -0
- package/dist/chunk-VHAA22YE.js.map +7 -0
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2596 -354
- package/dist/runtime.js.map +7 -0
- package/dist/schema.d.ts +90 -21
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +440 -151
- package/dist/schema.js.map +7 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -22
- package/dist/WorkflowTracker.d.ts +0 -10
- package/dist/WorkflowTracker.d.ts.map +0 -1
- package/dist/WorkflowTracker.js +0 -19
- package/dist/WorkflowWidget.d.ts +0 -70
- package/dist/WorkflowWidget.d.ts.map +0 -1
- package/dist/WorkflowWidget.js +0 -330
- package/dist/WorkflowWidgetLit.js +0 -617
- package/dist/cdn.d.ts +0 -35
- package/dist/cdn.d.ts.map +0 -1
- package/dist/cdn.js +0 -39
- package/dist/celebrations/__tests__/engine.test.js +0 -130
- package/dist/celebrations/__tests__/executor.test.js +0 -102
- package/dist/celebrations/__tests__/reduced-motion.test.js +0 -97
- package/dist/celebrations/effects/__tests__/confetti.test.js +0 -89
- package/dist/celebrations/effects/__tests__/emoji-rain.test.js +0 -88
- package/dist/celebrations/effects/__tests__/fireworks.test.js +0 -87
- package/dist/celebrations/effects/__tests__/sparkles.test.js +0 -79
- package/dist/celebrations/effects/confetti.js +0 -80
- package/dist/celebrations/effects/emoji-rain.js +0 -73
- package/dist/celebrations/effects/fireworks.js +0 -69
- package/dist/celebrations/effects/sparkles.js +0 -83
- package/dist/celebrations/engine.js +0 -93
- package/dist/celebrations/index.js +0 -73
- package/dist/celebrations/types.js +0 -1
- package/dist/editor.d.ts +0 -27
- package/dist/editor.d.ts.map +0 -1
- package/dist/editor.js +0 -22
- package/dist/executors/tour.js +0 -335
- package/dist/highlight.js +0 -180
- package/dist/modal.js +0 -218
- package/dist/overlay-editor-state.d.ts +0 -41
- package/dist/overlay-editor-state.d.ts.map +0 -1
- package/dist/overlay-editor-state.js +0 -131
- package/dist/overlay-editor-ui.d.ts +0 -9
- package/dist/overlay-editor-ui.d.ts.map +0 -1
- package/dist/overlay-editor-ui.js +0 -306
- package/dist/runtime-lit.d.ts +0 -94
- package/dist/runtime-lit.d.ts.map +0 -1
- package/dist/runtime-lit.js +0 -402
- package/dist/sanitizer.js +0 -84
- package/dist/summarize.js +0 -86
- package/dist/tooltip.js +0 -279
- package/dist/tour-types.js +0 -7
- package/dist/types.js +0 -7
- package/dist/workflow-types.js +0 -1
- package/node_modules/@syntro/design-system/README.md +0 -335
- package/node_modules/@syntro/design-system/dist/assets/syntrologie-logo.svg +0 -21
- package/node_modules/@syntro/design-system/dist/assets/syntrologie-logomark.svg +0 -10
- package/node_modules/@syntro/design-system/dist/index.d.ts +0 -8
- package/node_modules/@syntro/design-system/dist/index.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/index.js +0 -7
- package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts +0 -19
- package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/tailwind-preset.js +0 -455
- package/node_modules/@syntro/design-system/dist/tokens/colors.css +0 -464
- package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts +0 -874
- package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/tokens/colors.js +0 -564
- package/node_modules/@syntro/design-system/dist/tokens/effects.css +0 -43
- package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts +0 -139
- package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/tokens/effects.js +0 -121
- package/node_modules/@syntro/design-system/dist/tokens/index.d.ts +0 -12
- package/node_modules/@syntro/design-system/dist/tokens/index.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/tokens/index.js +0 -11
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts +0 -93
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts.map +0 -1
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.js +0 -72
- package/node_modules/@syntro/design-system/package.json +0 -55
- package/node_modules/@syntro/design-system/src/assets/syntrologie-logo.svg +0 -21
- package/node_modules/@syntro/design-system/src/assets/syntrologie-logomark.svg +0 -10
- package/node_modules/@syntrologie/shared-editor-ui/dist/cn.d.ts +0 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/cn.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/cn.js +0 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts +0 -34
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +0 -161
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts +0 -84
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.js +0 -323
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.js +0 -9
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts +0 -25
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.js +0 -55
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts +0 -23
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +0 -40
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts +0 -33
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.js +0 -118
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.js +0 -22
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts +0 -32
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.js +0 -68
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.js +0 -9
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts +0 -34
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.js +0 -57
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts +0 -13
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.js +0 -31
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.js +0 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.d.ts +0 -13
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.js +0 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts +0 -36
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.js +0 -102
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts +0 -20
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.js +0 -48
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.d.ts +0 -9
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts +0 -16
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.js +0 -25
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.js +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts +0 -66
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.js +0 -87
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts +0 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.js +0 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts +0 -25
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +0 -390
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts +0 -66
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.js +0 -528
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.js +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts +0 -41
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.js +0 -63
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.js +0 -17
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts +0 -55
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.js +0 -92
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.d.ts +0 -32
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +0 -85
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts +0 -90
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.js +0 -242
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.d.ts +0 -6
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.js +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts +0 -12
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.js +0 -21
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.js +0 -5
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts +0 -21
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.js +0 -33
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts +0 -12
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +0 -40
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts +0 -28
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.js +0 -121
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts +0 -110
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.js +0 -481
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts +0 -26
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +0 -202
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.d.ts +0 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.js +0 -46
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts +0 -24
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.js +0 -86
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +0 -36
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +0 -26
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts +0 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.js +0 -14
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts +0 -33
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.js +0 -68
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.d.ts +0 -22
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.d.ts.map +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.js +0 -143
- package/node_modules/@syntrologie/shared-editor-ui/package.json +0 -55
package/dist/cdn.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDN Entry Point for Adaptive Overlays
|
|
3
|
-
*
|
|
4
|
-
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
-
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
-
*/
|
|
7
|
-
import { editor } from './editor';
|
|
8
|
-
import { executors, runtime } from './runtime';
|
|
9
|
-
/**
|
|
10
|
-
* App manifest for registry registration.
|
|
11
|
-
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
12
|
-
*/
|
|
13
|
-
export const manifest = {
|
|
14
|
-
id: runtime.id,
|
|
15
|
-
version: runtime.version,
|
|
16
|
-
name: runtime.name,
|
|
17
|
-
description: runtime.description,
|
|
18
|
-
runtime: {
|
|
19
|
-
actions: executors.map(({ kind, executor }) => ({
|
|
20
|
-
kind,
|
|
21
|
-
executor,
|
|
22
|
-
})),
|
|
23
|
-
},
|
|
24
|
-
editor,
|
|
25
|
-
metadata: {
|
|
26
|
-
isBuiltIn: true,
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Self-register with global registry if available.
|
|
31
|
-
* This happens when loaded via script tag (UMD).
|
|
32
|
-
*/
|
|
33
|
-
if (typeof window !== 'undefined') {
|
|
34
|
-
const registry = window.SynOS?.appRegistry;
|
|
35
|
-
if (registry && typeof registry.register === 'function') {
|
|
36
|
-
registry.register(manifest);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export default manifest;
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
/** Stub CanvasRenderingContext2D for jsdom (which lacks canvas support) */
|
|
3
|
-
function stubCanvasContext() {
|
|
4
|
-
const ctx = {
|
|
5
|
-
scale: vi.fn(),
|
|
6
|
-
clearRect: vi.fn(),
|
|
7
|
-
save: vi.fn(),
|
|
8
|
-
restore: vi.fn(),
|
|
9
|
-
translate: vi.fn(),
|
|
10
|
-
rotate: vi.fn(),
|
|
11
|
-
fillRect: vi.fn(),
|
|
12
|
-
beginPath: vi.fn(),
|
|
13
|
-
arc: vi.fn(),
|
|
14
|
-
fill: vi.fn(),
|
|
15
|
-
fillText: vi.fn(),
|
|
16
|
-
globalAlpha: 1,
|
|
17
|
-
fillStyle: '',
|
|
18
|
-
font: '',
|
|
19
|
-
shadowBlur: 0,
|
|
20
|
-
shadowColor: '',
|
|
21
|
-
};
|
|
22
|
-
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(ctx);
|
|
23
|
-
return ctx;
|
|
24
|
-
}
|
|
25
|
-
function mockEffect() {
|
|
26
|
-
return {
|
|
27
|
-
init: vi.fn(() => [
|
|
28
|
-
{
|
|
29
|
-
x: 100,
|
|
30
|
-
y: 0,
|
|
31
|
-
vx: 0,
|
|
32
|
-
vy: 1,
|
|
33
|
-
rotation: 0,
|
|
34
|
-
rotationSpeed: 0,
|
|
35
|
-
size: 5,
|
|
36
|
-
color: '#ff0000',
|
|
37
|
-
opacity: 1,
|
|
38
|
-
},
|
|
39
|
-
]),
|
|
40
|
-
update: vi.fn(() => true),
|
|
41
|
-
render: vi.fn(),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
const defaultConfig = {
|
|
45
|
-
duration: 3000,
|
|
46
|
-
intensity: 'medium',
|
|
47
|
-
colors: ['#ff0000', '#00ff00'],
|
|
48
|
-
};
|
|
49
|
-
describe('CelebrationEngine', () => {
|
|
50
|
-
let container;
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
container = document.createElement('div');
|
|
53
|
-
document.body.appendChild(container);
|
|
54
|
-
stubCanvasContext();
|
|
55
|
-
vi.useFakeTimers();
|
|
56
|
-
});
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
vi.useRealTimers();
|
|
59
|
-
vi.restoreAllMocks();
|
|
60
|
-
container.remove();
|
|
61
|
-
});
|
|
62
|
-
it('creates a canvas element when started', async () => {
|
|
63
|
-
const { CelebrationEngine } = await import('../engine');
|
|
64
|
-
const engine = new CelebrationEngine();
|
|
65
|
-
const effect = mockEffect();
|
|
66
|
-
engine.start(container, effect, defaultConfig);
|
|
67
|
-
const canvas = container.querySelector('canvas[data-syntro-celebrate]');
|
|
68
|
-
expect(canvas).not.toBeNull();
|
|
69
|
-
expect(canvas?.style.position).toBe('fixed');
|
|
70
|
-
expect(canvas?.style.pointerEvents).toBe('none');
|
|
71
|
-
engine.stop();
|
|
72
|
-
});
|
|
73
|
-
it('removes canvas when stopped', async () => {
|
|
74
|
-
const { CelebrationEngine } = await import('../engine');
|
|
75
|
-
const engine = new CelebrationEngine();
|
|
76
|
-
const effect = mockEffect();
|
|
77
|
-
engine.start(container, effect, defaultConfig);
|
|
78
|
-
expect(container.querySelector('canvas[data-syntro-celebrate]')).not.toBeNull();
|
|
79
|
-
engine.stop();
|
|
80
|
-
expect(container.querySelector('canvas[data-syntro-celebrate]')).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
it('calls effect.init with canvas dimensions', async () => {
|
|
83
|
-
const { CelebrationEngine } = await import('../engine');
|
|
84
|
-
const engine = new CelebrationEngine();
|
|
85
|
-
const effect = mockEffect();
|
|
86
|
-
engine.start(container, effect, defaultConfig);
|
|
87
|
-
expect(effect.init).toHaveBeenCalledWith(expect.any(Number), expect.any(Number), defaultConfig);
|
|
88
|
-
engine.stop();
|
|
89
|
-
});
|
|
90
|
-
it('calls effect.update and effect.render on animation frame', async () => {
|
|
91
|
-
const { CelebrationEngine } = await import('../engine');
|
|
92
|
-
const engine = new CelebrationEngine();
|
|
93
|
-
const effect = mockEffect();
|
|
94
|
-
engine.start(container, effect, defaultConfig);
|
|
95
|
-
// Trigger an animation frame
|
|
96
|
-
vi.advanceTimersByTime(16);
|
|
97
|
-
expect(effect.update).toHaveBeenCalled();
|
|
98
|
-
expect(effect.render).toHaveBeenCalled();
|
|
99
|
-
engine.stop();
|
|
100
|
-
});
|
|
101
|
-
it('auto-stops after duration expires', async () => {
|
|
102
|
-
const { CelebrationEngine } = await import('../engine');
|
|
103
|
-
const engine = new CelebrationEngine();
|
|
104
|
-
const effect = mockEffect();
|
|
105
|
-
engine.start(container, effect, { ...defaultConfig, duration: 1000 });
|
|
106
|
-
// Advance past duration
|
|
107
|
-
vi.advanceTimersByTime(1100);
|
|
108
|
-
expect(container.querySelector('canvas[data-syntro-celebrate]')).toBeNull();
|
|
109
|
-
});
|
|
110
|
-
it('auto-stops when effect.update returns false', async () => {
|
|
111
|
-
const { CelebrationEngine } = await import('../engine');
|
|
112
|
-
const engine = new CelebrationEngine();
|
|
113
|
-
const effect = mockEffect();
|
|
114
|
-
// After first frame, update returns false (all particles done)
|
|
115
|
-
effect.update.mockReturnValue(false);
|
|
116
|
-
engine.start(container, effect, defaultConfig);
|
|
117
|
-
// Trigger animation frame
|
|
118
|
-
vi.advanceTimersByTime(16);
|
|
119
|
-
expect(container.querySelector('canvas[data-syntro-celebrate]')).toBeNull();
|
|
120
|
-
});
|
|
121
|
-
it('stop() is safe to call multiple times', async () => {
|
|
122
|
-
const { CelebrationEngine } = await import('../engine');
|
|
123
|
-
const engine = new CelebrationEngine();
|
|
124
|
-
const effect = mockEffect();
|
|
125
|
-
engine.start(container, effect, defaultConfig);
|
|
126
|
-
engine.stop();
|
|
127
|
-
engine.stop(); // should not throw
|
|
128
|
-
expect(container.querySelector('canvas[data-syntro-celebrate]')).toBeNull();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
/** Stub CanvasRenderingContext2D for jsdom */
|
|
3
|
-
function stubCanvasContext() {
|
|
4
|
-
const ctx = {
|
|
5
|
-
scale: vi.fn(),
|
|
6
|
-
clearRect: vi.fn(),
|
|
7
|
-
save: vi.fn(),
|
|
8
|
-
restore: vi.fn(),
|
|
9
|
-
translate: vi.fn(),
|
|
10
|
-
rotate: vi.fn(),
|
|
11
|
-
fillRect: vi.fn(),
|
|
12
|
-
beginPath: vi.fn(),
|
|
13
|
-
arc: vi.fn(),
|
|
14
|
-
fill: vi.fn(),
|
|
15
|
-
fillText: vi.fn(),
|
|
16
|
-
moveTo: vi.fn(),
|
|
17
|
-
lineTo: vi.fn(),
|
|
18
|
-
closePath: vi.fn(),
|
|
19
|
-
globalAlpha: 1,
|
|
20
|
-
fillStyle: '',
|
|
21
|
-
font: '',
|
|
22
|
-
shadowBlur: 0,
|
|
23
|
-
shadowColor: '',
|
|
24
|
-
textAlign: 'center',
|
|
25
|
-
textBaseline: 'middle',
|
|
26
|
-
};
|
|
27
|
-
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(ctx);
|
|
28
|
-
return ctx;
|
|
29
|
-
}
|
|
30
|
-
function mockContext() {
|
|
31
|
-
const overlayRoot = document.createElement('div');
|
|
32
|
-
document.body.appendChild(overlayRoot);
|
|
33
|
-
return {
|
|
34
|
-
overlayRoot,
|
|
35
|
-
resolveAnchor: vi.fn(() => null),
|
|
36
|
-
generateId: vi.fn(() => 'test-id'),
|
|
37
|
-
publishEvent: vi.fn(),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
describe('executeCelebrate', () => {
|
|
41
|
-
let context;
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
context = mockContext();
|
|
44
|
-
stubCanvasContext();
|
|
45
|
-
vi.useFakeTimers();
|
|
46
|
-
});
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
vi.useRealTimers();
|
|
49
|
-
vi.restoreAllMocks();
|
|
50
|
-
context.overlayRoot.remove();
|
|
51
|
-
});
|
|
52
|
-
it('creates engine and starts confetti effect', async () => {
|
|
53
|
-
const { executeCelebrate } = await import('../index');
|
|
54
|
-
const result = await executeCelebrate({ kind: 'overlays:celebrate', effect: 'confetti' }, context);
|
|
55
|
-
const canvas = context.overlayRoot.querySelector('canvas[data-syntro-celebrate]');
|
|
56
|
-
expect(canvas).not.toBeNull();
|
|
57
|
-
result.cleanup();
|
|
58
|
-
});
|
|
59
|
-
it('returns cleanup that stops the engine', async () => {
|
|
60
|
-
const { executeCelebrate } = await import('../index');
|
|
61
|
-
const result = await executeCelebrate({ kind: 'overlays:celebrate', effect: 'confetti' }, context);
|
|
62
|
-
result.cleanup();
|
|
63
|
-
const canvas = context.overlayRoot.querySelector('canvas[data-syntro-celebrate]');
|
|
64
|
-
expect(canvas).toBeNull();
|
|
65
|
-
});
|
|
66
|
-
it('handles unknown effect name gracefully', async () => {
|
|
67
|
-
const { executeCelebrate } = await import('../index');
|
|
68
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
69
|
-
const result = await executeCelebrate({ kind: 'overlays:celebrate', effect: 'unknown-effect' }, context);
|
|
70
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('unknown-effect'));
|
|
71
|
-
// Should still return a valid cleanup
|
|
72
|
-
expect(typeof result.cleanup).toBe('function');
|
|
73
|
-
result.cleanup();
|
|
74
|
-
warnSpy.mockRestore();
|
|
75
|
-
});
|
|
76
|
-
it('publishes action.applied event', async () => {
|
|
77
|
-
const { executeCelebrate } = await import('../index');
|
|
78
|
-
await executeCelebrate({ kind: 'overlays:celebrate', effect: 'confetti' }, context);
|
|
79
|
-
expect(context.publishEvent).toHaveBeenCalledWith('action.applied', expect.objectContaining({ kind: 'overlays:celebrate', effect: 'confetti' }));
|
|
80
|
-
// Cleanup
|
|
81
|
-
const canvas = context.overlayRoot.querySelector('canvas[data-syntro-celebrate]');
|
|
82
|
-
canvas?.remove();
|
|
83
|
-
});
|
|
84
|
-
it('uses default config values when not specified', async () => {
|
|
85
|
-
const { executeCelebrate } = await import('../index');
|
|
86
|
-
const result = await executeCelebrate({ kind: 'overlays:celebrate', effect: 'fireworks' }, context);
|
|
87
|
-
// Should not throw — defaults are applied
|
|
88
|
-
const canvas = context.overlayRoot.querySelector('canvas[data-syntro-celebrate]');
|
|
89
|
-
expect(canvas).not.toBeNull();
|
|
90
|
-
result.cleanup();
|
|
91
|
-
});
|
|
92
|
-
it('supports all registered effect names', async () => {
|
|
93
|
-
const { executeCelebrate } = await import('../index');
|
|
94
|
-
const effectNames = ['confetti', 'fireworks', 'sparkles', 'emoji-rain'];
|
|
95
|
-
for (const effect of effectNames) {
|
|
96
|
-
const result = await executeCelebrate({ kind: 'overlays:celebrate', effect }, context);
|
|
97
|
-
const canvas = context.overlayRoot.querySelector('canvas[data-syntro-celebrate]');
|
|
98
|
-
expect(canvas).not.toBeNull();
|
|
99
|
-
result.cleanup();
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
/** Stub CanvasRenderingContext2D for jsdom (which lacks canvas support) */
|
|
3
|
-
function stubCanvasContext() {
|
|
4
|
-
const ctx = {
|
|
5
|
-
scale: vi.fn(),
|
|
6
|
-
clearRect: vi.fn(),
|
|
7
|
-
save: vi.fn(),
|
|
8
|
-
restore: vi.fn(),
|
|
9
|
-
translate: vi.fn(),
|
|
10
|
-
rotate: vi.fn(),
|
|
11
|
-
fillRect: vi.fn(),
|
|
12
|
-
beginPath: vi.fn(),
|
|
13
|
-
arc: vi.fn(),
|
|
14
|
-
fill: vi.fn(),
|
|
15
|
-
fillText: vi.fn(),
|
|
16
|
-
globalAlpha: 1,
|
|
17
|
-
fillStyle: '',
|
|
18
|
-
font: '',
|
|
19
|
-
shadowBlur: 0,
|
|
20
|
-
shadowColor: '',
|
|
21
|
-
};
|
|
22
|
-
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(ctx);
|
|
23
|
-
return ctx;
|
|
24
|
-
}
|
|
25
|
-
function mockEffect() {
|
|
26
|
-
return {
|
|
27
|
-
init: vi.fn(() => [
|
|
28
|
-
{
|
|
29
|
-
x: 100,
|
|
30
|
-
y: 0,
|
|
31
|
-
vx: 0,
|
|
32
|
-
vy: 1,
|
|
33
|
-
rotation: 0,
|
|
34
|
-
rotationSpeed: 0,
|
|
35
|
-
size: 5,
|
|
36
|
-
color: '#ff0000',
|
|
37
|
-
opacity: 1,
|
|
38
|
-
},
|
|
39
|
-
]),
|
|
40
|
-
update: vi.fn(() => true),
|
|
41
|
-
render: vi.fn(),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
const defaultConfig = {
|
|
45
|
-
duration: 3000,
|
|
46
|
-
intensity: 'medium',
|
|
47
|
-
colors: ['#ff0000', '#00ff00'],
|
|
48
|
-
};
|
|
49
|
-
describe('CelebrationEngine prefers-reduced-motion', () => {
|
|
50
|
-
let container;
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
container = document.createElement('div');
|
|
53
|
-
document.body.appendChild(container);
|
|
54
|
-
stubCanvasContext();
|
|
55
|
-
vi.useFakeTimers();
|
|
56
|
-
});
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
vi.useRealTimers();
|
|
59
|
-
vi.restoreAllMocks();
|
|
60
|
-
container.remove();
|
|
61
|
-
});
|
|
62
|
-
it('should skip animation when prefers-reduced-motion is "reduce"', async () => {
|
|
63
|
-
window.matchMedia = vi.fn().mockReturnValue({ matches: true });
|
|
64
|
-
const { CelebrationEngine } = await import('../engine');
|
|
65
|
-
const engine = new CelebrationEngine();
|
|
66
|
-
const effect = mockEffect();
|
|
67
|
-
engine.start(container, effect, defaultConfig);
|
|
68
|
-
// Effect should never be initialized or rendered
|
|
69
|
-
expect(effect.init).not.toHaveBeenCalled();
|
|
70
|
-
expect(effect.render).not.toHaveBeenCalled();
|
|
71
|
-
// matchMedia should have been queried with the right media query
|
|
72
|
-
expect(window.matchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)');
|
|
73
|
-
});
|
|
74
|
-
it('should run animation when prefers-reduced-motion is not set', async () => {
|
|
75
|
-
window.matchMedia = vi.fn().mockReturnValue({ matches: false });
|
|
76
|
-
const { CelebrationEngine } = await import('../engine');
|
|
77
|
-
const engine = new CelebrationEngine();
|
|
78
|
-
const effect = mockEffect();
|
|
79
|
-
engine.start(container, effect, defaultConfig);
|
|
80
|
-
// Effect should be initialized normally
|
|
81
|
-
expect(effect.init).toHaveBeenCalled();
|
|
82
|
-
// Canvas should be created
|
|
83
|
-
const canvas = container.querySelector('canvas[data-syntro-celebrate]');
|
|
84
|
-
expect(canvas).not.toBeNull();
|
|
85
|
-
engine.stop();
|
|
86
|
-
});
|
|
87
|
-
it('should not create canvas element when motion is reduced', async () => {
|
|
88
|
-
window.matchMedia = vi.fn().mockReturnValue({ matches: true });
|
|
89
|
-
const { CelebrationEngine } = await import('../engine');
|
|
90
|
-
const engine = new CelebrationEngine();
|
|
91
|
-
const effect = mockEffect();
|
|
92
|
-
engine.start(container, effect, defaultConfig);
|
|
93
|
-
// No canvas should exist in the container
|
|
94
|
-
const canvas = container.querySelector('canvas[data-syntro-celebrate]');
|
|
95
|
-
expect(canvas).toBeNull();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
const defaultConfig = {
|
|
3
|
-
duration: 3000,
|
|
4
|
-
intensity: 'medium',
|
|
5
|
-
colors: ['#ff0000', '#00ff00', '#0000ff'],
|
|
6
|
-
};
|
|
7
|
-
describe('confetti effect', () => {
|
|
8
|
-
describe('init', () => {
|
|
9
|
-
it('creates correct particle count for light intensity', async () => {
|
|
10
|
-
const { confettiEffect } = await import('../confetti');
|
|
11
|
-
const particles = confettiEffect.init(800, 600, { ...defaultConfig, intensity: 'light' });
|
|
12
|
-
expect(particles).toHaveLength(50);
|
|
13
|
-
});
|
|
14
|
-
it('creates correct particle count for medium intensity', async () => {
|
|
15
|
-
const { confettiEffect } = await import('../confetti');
|
|
16
|
-
const particles = confettiEffect.init(800, 600, { ...defaultConfig, intensity: 'medium' });
|
|
17
|
-
expect(particles).toHaveLength(100);
|
|
18
|
-
});
|
|
19
|
-
it('creates correct particle count for heavy intensity', async () => {
|
|
20
|
-
const { confettiEffect } = await import('../confetti');
|
|
21
|
-
const particles = confettiEffect.init(800, 600, { ...defaultConfig, intensity: 'heavy' });
|
|
22
|
-
expect(particles).toHaveLength(200);
|
|
23
|
-
});
|
|
24
|
-
it('uses colors from config', async () => {
|
|
25
|
-
const { confettiEffect } = await import('../confetti');
|
|
26
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
27
|
-
const usedColors = new Set(particles.map((p) => p.color));
|
|
28
|
-
// All particle colors should be from the provided palette
|
|
29
|
-
for (const color of usedColors) {
|
|
30
|
-
expect(defaultConfig.colors).toContain(color);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
it('creates particles with rect or circle shapes', async () => {
|
|
34
|
-
const { confettiEffect } = await import('../confetti');
|
|
35
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
36
|
-
const shapes = new Set(particles.map((p) => p.shape));
|
|
37
|
-
expect(shapes.size).toBeGreaterThanOrEqual(1);
|
|
38
|
-
for (const shape of shapes) {
|
|
39
|
-
expect(['rect', 'circle']).toContain(shape);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
describe('update', () => {
|
|
44
|
-
it('applies gravity (vy increases each frame)', async () => {
|
|
45
|
-
const { confettiEffect } = await import('../confetti');
|
|
46
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
47
|
-
const initialVy = particles[0].vy;
|
|
48
|
-
confettiEffect.update(particles, 16, 16);
|
|
49
|
-
expect(particles[0].vy).toBeGreaterThan(initialVy);
|
|
50
|
-
});
|
|
51
|
-
it('returns true while particles are still visible', async () => {
|
|
52
|
-
const { confettiEffect } = await import('../confetti');
|
|
53
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
54
|
-
const alive = confettiEffect.update(particles, 16, 16);
|
|
55
|
-
expect(alive).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it('returns false when all particles have faded', async () => {
|
|
58
|
-
const { confettiEffect } = await import('../confetti');
|
|
59
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
60
|
-
// Force all particles to be fully faded
|
|
61
|
-
for (const p of particles) {
|
|
62
|
-
p.opacity = 0;
|
|
63
|
-
}
|
|
64
|
-
const alive = confettiEffect.update(particles, 16, 5000);
|
|
65
|
-
expect(alive).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe('render', () => {
|
|
69
|
-
it('draws to canvas context', async () => {
|
|
70
|
-
const { confettiEffect } = await import('../confetti');
|
|
71
|
-
const particles = confettiEffect.init(800, 600, defaultConfig);
|
|
72
|
-
const ctx = {
|
|
73
|
-
save: vi.fn(),
|
|
74
|
-
restore: vi.fn(),
|
|
75
|
-
translate: vi.fn(),
|
|
76
|
-
rotate: vi.fn(),
|
|
77
|
-
fillRect: vi.fn(),
|
|
78
|
-
beginPath: vi.fn(),
|
|
79
|
-
arc: vi.fn(),
|
|
80
|
-
fill: vi.fn(),
|
|
81
|
-
globalAlpha: 1,
|
|
82
|
-
fillStyle: '',
|
|
83
|
-
};
|
|
84
|
-
confettiEffect.render(ctx, particles);
|
|
85
|
-
expect(ctx.save).toHaveBeenCalled();
|
|
86
|
-
expect(ctx.restore).toHaveBeenCalled();
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
const defaultConfig = {
|
|
3
|
-
duration: 3000,
|
|
4
|
-
intensity: 'medium',
|
|
5
|
-
colors: ['#000000'],
|
|
6
|
-
};
|
|
7
|
-
describe('emoji-rain effect', () => {
|
|
8
|
-
describe('init', () => {
|
|
9
|
-
it('creates emoji particles across the top of the screen', async () => {
|
|
10
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
11
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
12
|
-
expect(particles.length).toBeGreaterThan(0);
|
|
13
|
-
for (const p of particles) {
|
|
14
|
-
expect(p.shape).toBe('emoji');
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
it('uses default emoji when none specified in props', async () => {
|
|
18
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
19
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
20
|
-
for (const p of particles) {
|
|
21
|
-
expect(p.emoji).toBeDefined();
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
it('uses emoji from config.props when provided', async () => {
|
|
25
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
26
|
-
const particles = emojiRainEffect.init(800, 600, {
|
|
27
|
-
...defaultConfig,
|
|
28
|
-
props: { emoji: '🔥' },
|
|
29
|
-
});
|
|
30
|
-
for (const p of particles) {
|
|
31
|
-
expect(p.emoji).toBe('🔥');
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
it('scales count with intensity', async () => {
|
|
35
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
36
|
-
const light = emojiRainEffect.init(800, 600, { ...defaultConfig, intensity: 'light' });
|
|
37
|
-
const heavy = emojiRainEffect.init(800, 600, { ...defaultConfig, intensity: 'heavy' });
|
|
38
|
-
expect(heavy.length).toBeGreaterThan(light.length);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe('update', () => {
|
|
42
|
-
it('moves particles downward (vy is positive)', async () => {
|
|
43
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
44
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
45
|
-
const initialY = particles[0].y;
|
|
46
|
-
emojiRainEffect.update(particles, 16, 16);
|
|
47
|
-
expect(particles[0].y).toBeGreaterThan(initialY);
|
|
48
|
-
});
|
|
49
|
-
it('applies horizontal wobble', async () => {
|
|
50
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
51
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
52
|
-
const initialX = particles[0].x;
|
|
53
|
-
// Run several frames to see wobble effect
|
|
54
|
-
for (let i = 0; i < 10; i++) {
|
|
55
|
-
emojiRainEffect.update(particles, 16, i * 16);
|
|
56
|
-
}
|
|
57
|
-
// X should have changed due to wobble
|
|
58
|
-
expect(particles[0].x).not.toBe(initialX);
|
|
59
|
-
});
|
|
60
|
-
it('returns false when all particles have faded', async () => {
|
|
61
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
62
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
63
|
-
for (const p of particles) {
|
|
64
|
-
p.opacity = 0;
|
|
65
|
-
}
|
|
66
|
-
expect(emojiRainEffect.update(particles, 16, 5000)).toBe(false);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe('render', () => {
|
|
70
|
-
it('renders emoji via fillText', async () => {
|
|
71
|
-
const { emojiRainEffect } = await import('../emoji-rain');
|
|
72
|
-
const particles = emojiRainEffect.init(800, 600, defaultConfig);
|
|
73
|
-
const ctx = {
|
|
74
|
-
save: vi.fn(),
|
|
75
|
-
restore: vi.fn(),
|
|
76
|
-
fillText: vi.fn(),
|
|
77
|
-
globalAlpha: 1,
|
|
78
|
-
font: '',
|
|
79
|
-
textAlign: 'center',
|
|
80
|
-
textBaseline: 'middle',
|
|
81
|
-
};
|
|
82
|
-
emojiRainEffect.render(ctx, particles);
|
|
83
|
-
expect(ctx.fillText).toHaveBeenCalled();
|
|
84
|
-
expect(ctx.save).toHaveBeenCalled();
|
|
85
|
-
expect(ctx.restore).toHaveBeenCalled();
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
const defaultConfig = {
|
|
3
|
-
duration: 3000,
|
|
4
|
-
intensity: 'medium',
|
|
5
|
-
colors: ['#ff0000', '#00ff00', '#0000ff'],
|
|
6
|
-
};
|
|
7
|
-
describe('fireworks effect', () => {
|
|
8
|
-
describe('init', () => {
|
|
9
|
-
it('creates burst groups with particles radiating outward', async () => {
|
|
10
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
11
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
12
|
-
// Medium intensity: 3-5 bursts × 40 particles each = 120-200
|
|
13
|
-
expect(particles.length).toBeGreaterThanOrEqual(60);
|
|
14
|
-
expect(particles.length).toBeLessThanOrEqual(400);
|
|
15
|
-
});
|
|
16
|
-
it('scales particle count with intensity', async () => {
|
|
17
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
18
|
-
const light = fireworksEffect.init(800, 600, { ...defaultConfig, intensity: 'light' });
|
|
19
|
-
const heavy = fireworksEffect.init(800, 600, { ...defaultConfig, intensity: 'heavy' });
|
|
20
|
-
expect(heavy.length).toBeGreaterThan(light.length);
|
|
21
|
-
});
|
|
22
|
-
it('positions bursts in upper 60% of screen', async () => {
|
|
23
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
24
|
-
const height = 600;
|
|
25
|
-
const particles = fireworksEffect.init(800, height, defaultConfig);
|
|
26
|
-
// Burst centers (stored in data.centerY) should be in upper 60%
|
|
27
|
-
for (const p of particles) {
|
|
28
|
-
if (p.data?.centerY !== undefined) {
|
|
29
|
-
expect(p.data.centerY).toBeLessThanOrEqual(height * 0.6);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
it('uses colors from config', async () => {
|
|
34
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
35
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
36
|
-
const usedColors = new Set(particles.map((p) => p.color));
|
|
37
|
-
for (const color of usedColors) {
|
|
38
|
-
expect(defaultConfig.colors).toContain(color);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('update', () => {
|
|
43
|
-
it('applies deceleration (velocity decreases each frame)', async () => {
|
|
44
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
45
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
46
|
-
const initialSpeed = Math.sqrt(particles[0].vx ** 2 + particles[0].vy ** 2);
|
|
47
|
-
fireworksEffect.update(particles, 16, 16);
|
|
48
|
-
const newSpeed = Math.sqrt(particles[0].vx ** 2 + particles[0].vy ** 2);
|
|
49
|
-
expect(newSpeed).toBeLessThan(initialSpeed);
|
|
50
|
-
});
|
|
51
|
-
it('returns true while particles are visible', async () => {
|
|
52
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
53
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
54
|
-
expect(fireworksEffect.update(particles, 16, 16)).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it('returns false when all particles have faded', async () => {
|
|
57
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
58
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
59
|
-
for (const p of particles) {
|
|
60
|
-
p.opacity = 0;
|
|
61
|
-
}
|
|
62
|
-
expect(fireworksEffect.update(particles, 16, 5000)).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe('render', () => {
|
|
66
|
-
it('draws circles with glow effect', async () => {
|
|
67
|
-
const { fireworksEffect } = await import('../fireworks');
|
|
68
|
-
const particles = fireworksEffect.init(800, 600, defaultConfig);
|
|
69
|
-
const ctx = {
|
|
70
|
-
save: vi.fn(),
|
|
71
|
-
restore: vi.fn(),
|
|
72
|
-
beginPath: vi.fn(),
|
|
73
|
-
arc: vi.fn(),
|
|
74
|
-
fill: vi.fn(),
|
|
75
|
-
globalAlpha: 1,
|
|
76
|
-
fillStyle: '',
|
|
77
|
-
shadowBlur: 0,
|
|
78
|
-
shadowColor: '',
|
|
79
|
-
};
|
|
80
|
-
fireworksEffect.render(ctx, particles);
|
|
81
|
-
expect(ctx.save).toHaveBeenCalled();
|
|
82
|
-
expect(ctx.beginPath).toHaveBeenCalled();
|
|
83
|
-
expect(ctx.arc).toHaveBeenCalled();
|
|
84
|
-
expect(ctx.restore).toHaveBeenCalled();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|