@blotch/dolan 22.2.3 → 22.2.4
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/commands/add.js +9 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +36 -3
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/doctor.js +187 -7
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +280 -7
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +38 -18
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +107 -1
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/theme.js +1 -1
- package/dist/commands/theme.js.map +1 -1
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.js +126 -45
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +314 -2
- package/dist/commands/update.test.js.map +1 -1
- package/dist/lockfile.d.ts +7 -0
- package/dist/lockfile.js +36 -0
- package/dist/lockfile.js.map +1 -1
- package/dist/lockfile.test.d.ts +1 -0
- package/dist/lockfile.test.js +25 -0
- package/dist/lockfile.test.js.map +1 -0
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +48 -2
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +45 -3
- package/dist/registry.test.js.map +1 -1
- package/dist/test-utils.d.ts +5 -0
- package/dist/test-utils.js +6 -1
- package/dist/test-utils.js.map +1 -1
- package/dist/testing/fire-lynx-event.d.ts +14 -0
- package/dist/testing/fire-lynx-event.js +19 -0
- package/dist/testing/fire-lynx-event.js.map +1 -0
- package/dist/testing/fire-lynx-event.test.d.ts +1 -0
- package/dist/testing/fire-lynx-event.test.js +44 -0
- package/dist/testing/fire-lynx-event.test.js.map +1 -0
- package/dist/testing/lynx-main-thread.d.ts +22 -0
- package/dist/testing/lynx-main-thread.js +11 -0
- package/dist/testing/lynx-main-thread.js.map +1 -0
- package/dist/testing/lynx-main-thread.test.d.ts +1 -0
- package/dist/testing/lynx-main-thread.test.js +24 -0
- package/dist/testing/lynx-main-thread.test.js.map +1 -0
- package/dist/testing/set-input-signal.d.ts +5 -4
- package/dist/testing/set-input-signal.js +5 -4
- package/dist/testing/set-input-signal.js.map +1 -1
- package/dist/ui/components/accordion/accordion.test.ts +158 -144
- package/dist/ui/components/accordion/accordion.ts +19 -14
- package/dist/ui/components/action-sheet/action-sheet.test.ts +272 -187
- package/dist/ui/components/action-sheet/action-sheet.ts +40 -34
- package/dist/ui/components/alert/alert.test.ts +67 -19
- package/dist/ui/components/alert/alert.ts +14 -16
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +206 -139
- package/dist/ui/components/alert-dialog/alert-dialog.ts +32 -26
- package/dist/ui/components/area-chart/area-chart.test.ts +81 -1
- package/dist/ui/components/avatar/avatar.test.ts +89 -45
- package/dist/ui/components/badge/badge.test.ts +57 -23
- package/dist/ui/components/badge/badge.ts +12 -14
- package/dist/ui/components/bar-chart/bar-chart.test.ts +82 -1
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +373 -297
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +34 -33
- package/dist/ui/components/button/button.test.ts +611 -2
- package/dist/ui/components/button/button.ts +431 -62
- package/dist/ui/components/button/index.ts +2 -0
- package/dist/ui/components/button-group/button-group.test.ts +484 -2
- package/dist/ui/components/button-group/button-group.ts +380 -68
- package/dist/ui/components/button-group/index.ts +3 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +82 -1
- package/dist/ui/components/card/card.test.ts +82 -62
- package/dist/ui/components/chart/chart-presets.test.ts +197 -1
- package/dist/ui/components/chart/chart-presets.ts +8 -1
- package/dist/ui/components/chart/chart-theme.ts +12 -0
- package/dist/ui/components/chart/chart.test.ts +1017 -0
- package/dist/ui/components/chart/chart.ts +56 -43
- package/dist/ui/components/checkbox/checkbox.test.ts +124 -42
- package/dist/ui/components/checkbox/checkbox.ts +23 -23
- package/dist/ui/components/collapsible/collapsible.test.ts +158 -81
- package/dist/ui/components/collapsible/collapsible.ts +19 -14
- package/dist/ui/components/context-menu/context-menu.test.ts +224 -0
- package/dist/ui/components/context-menu/context-menu.ts +110 -0
- package/dist/ui/components/context-menu/index.ts +1 -0
- package/dist/ui/components/dialog/dialog.test.ts +210 -152
- package/dist/ui/components/dialog/dialog.ts +29 -23
- package/dist/ui/components/dock/dock-scroll.test.ts +130 -0
- package/dist/ui/components/dock/dock-scroll.ts +133 -0
- package/dist/ui/components/dock/dock.test.ts +749 -0
- package/dist/ui/components/dock/dock.ts +960 -0
- package/dist/ui/components/dock/index.ts +2 -0
- package/dist/ui/components/empty-state/empty-state.test.ts +28 -39
- package/dist/ui/components/glass/concentric-radius.test.ts +13 -0
- package/dist/ui/components/glass/concentric-radius.ts +17 -0
- package/dist/ui/components/glass/glass-design.test.ts +78 -0
- package/dist/ui/components/glass/glass-design.ts +119 -0
- package/dist/ui/components/glass/glass.test.ts +338 -0
- package/dist/ui/components/glass/glass.ts +360 -0
- package/dist/ui/components/glass/index.ts +18 -0
- package/dist/ui/components/icon/icon-theme.test.ts +36 -0
- package/dist/ui/components/icon/icon-theme.ts +36 -13
- package/dist/ui/components/icon/icon.test.ts +52 -41
- package/dist/ui/components/input/input.test.ts +178 -101
- package/dist/ui/components/input/input.ts +15 -12
- package/dist/ui/components/label/label.test.ts +11 -24
- package/dist/ui/components/line-chart/line-chart.test.ts +76 -1
- package/dist/ui/components/list/list.test.ts +80 -49
- package/dist/ui/components/menu/index.ts +25 -0
- package/dist/ui/components/menu/menu-placement.test.ts +153 -0
- package/dist/ui/components/menu/menu-placement.ts +159 -0
- package/dist/ui/components/menu/menu-popup.test.ts +632 -0
- package/dist/ui/components/menu/menu-popup.ts +544 -0
- package/dist/ui/components/menu/menu.test.ts +429 -0
- package/dist/ui/components/menu/menu.ts +415 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +425 -260
- package/dist/ui/components/nav-drawer/nav-drawer.ts +72 -56
- package/dist/ui/components/pagination/pagination.test.ts +136 -86
- package/dist/ui/components/progress/progress.test.ts +102 -46
- package/dist/ui/components/progress/progress.ts +52 -54
- package/dist/ui/components/radio-group/radio-group.test.ts +128 -83
- package/dist/ui/components/radio-group/radio-group.ts +24 -24
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +74 -1
- package/dist/ui/components/scroll-area/scroll-area.test.ts +72 -36
- package/dist/ui/components/select/select.test.ts +138 -78
- package/dist/ui/components/select/select.ts +17 -11
- package/dist/ui/components/separator/separator.test.ts +18 -17
- package/dist/ui/components/sheet/sheet.test.ts +64 -11
- package/dist/ui/components/sheet/sheet.ts +9 -1
- package/dist/ui/components/skeleton/skeleton.test.ts +24 -7
- package/dist/ui/components/skeleton/skeleton.ts +10 -16
- package/dist/ui/components/spinner/spinner.test.ts +64 -46
- package/dist/ui/components/spinner/spinner.ts +16 -19
- package/dist/ui/components/switch/switch.test.ts +116 -46
- package/dist/ui/components/switch/switch.ts +8 -13
- package/dist/ui/components/tab-bar/index.ts +1 -0
- package/dist/ui/components/tab-bar/tab-bar.test.ts +280 -0
- package/dist/ui/components/tab-bar/tab-bar.ts +230 -0
- package/dist/ui/components/tabs/tabs.test.ts +215 -157
- package/dist/ui/components/tabs/tabs.ts +110 -118
- package/dist/ui/components/textarea/textarea.test.ts +206 -100
- package/dist/ui/components/textarea/textarea.ts +14 -12
- package/dist/ui/components/toast/toast.test.ts +434 -350
- package/dist/ui/components/toast/toast.ts +42 -51
- package/dist/ui/components/toggle/toggle.test.ts +247 -140
- package/dist/ui/theme/chart-colors.test.ts +8 -3
- package/dist/ui/theme/dark.css +38 -2
- package/dist/ui/theme/default.css +55 -7
- package/dist/ui/theme/icon-colors.test.ts +8 -3
- package/dist/ui/theme/rgb-channels.test.ts +84 -0
- package/dist/ui/theme/tailwind-build.test.ts +114 -0
- package/dist/ui/theme/tailwind-css.test.ts +83 -0
- package/dist/ui/theme/tailwind-plugin.test.ts +42 -2
- package/dist/ui/theme/tailwind-plugin.ts +93 -29
- package/dist/ui/theme/tailwind.css +113 -0
- package/dist/ui/utils/animate.test.ts +13 -1
- package/dist/utils/analyze.d.ts +14 -0
- package/dist/utils/analyze.js +15 -0
- package/dist/utils/analyze.js.map +1 -1
- package/dist/utils/analyze.test.js +14 -1
- package/dist/utils/analyze.test.js.map +1 -1
- package/dist/utils/installed.d.ts +12 -0
- package/dist/utils/installed.js +16 -0
- package/dist/utils/installed.js.map +1 -1
- package/dist/utils/installed.test.js +17 -1
- package/dist/utils/installed.test.js.map +1 -1
- package/dist/utils/resolve-paths.d.ts +4 -4
- package/dist/utils/resolve-paths.js +5 -7
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +9 -5
- package/dist/utils/resolve-paths.test.js.map +1 -1
- package/dist/utils/tailwind.d.ts +25 -0
- package/dist/utils/tailwind.js +51 -0
- package/dist/utils/tailwind.js.map +1 -0
- package/dist/utils/tailwind.test.d.ts +1 -0
- package/dist/utils/tailwind.test.js +71 -0
- package/dist/utils/tailwind.test.js.map +1 -0
- package/package.json +9 -1
- package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -63
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -58
- package/dist/ui/components/button/button-render.test.ts +0 -132
- package/dist/ui/components/button-group/button-group-render.test.ts +0 -146
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +0 -59
- package/dist/ui/components/chart/chart-render.test.ts +0 -730
- package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -83
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -64
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import { Component, signal } from '@angular/core';
|
|
2
|
-
import {
|
|
1
|
+
import { Component, signal, viewChild } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
fireEvent,
|
|
4
|
+
render,
|
|
5
|
+
waitForUpdate,
|
|
6
|
+
} from '@blotch/angular-lynx-testing-library';
|
|
3
7
|
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type MockInstance,
|
|
10
|
+
afterEach,
|
|
11
|
+
beforeEach,
|
|
12
|
+
describe,
|
|
13
|
+
expect,
|
|
14
|
+
it,
|
|
15
|
+
vi,
|
|
16
|
+
} from 'vitest';
|
|
5
17
|
import { DURATION } from '../../utils/animate';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
18
|
+
import { fireLynxEvent } from '../../../test-utils/fire-lynx-event';
|
|
19
|
+
import {
|
|
20
|
+
type LynxMainThread,
|
|
21
|
+
lynxMainThread,
|
|
22
|
+
} from '../../../test-utils/lynx-main-thread';
|
|
23
|
+
import { UiIcon } from '../icon/icon';
|
|
8
24
|
import {
|
|
9
25
|
UiNavDrawer,
|
|
10
26
|
UiNavDrawerContent,
|
|
@@ -22,325 +38,474 @@ import {
|
|
|
22
38
|
UiNavDrawerItem,
|
|
23
39
|
UiNavDrawerFooter,
|
|
24
40
|
UiNavDrawerTrigger,
|
|
41
|
+
UiIcon,
|
|
25
42
|
LYNX_ELEMENTS,
|
|
26
43
|
],
|
|
27
44
|
template: `
|
|
28
|
-
<ui-nav-drawer
|
|
29
|
-
|
|
30
|
-
<ui-nav-drawer
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
<ui-nav-drawer-trigger class="ml-2" (pressed)="open.set(true)" />
|
|
46
|
+
@if (shown()) {
|
|
47
|
+
<ui-nav-drawer
|
|
48
|
+
[(open)]="open"
|
|
49
|
+
[side]="side()"
|
|
50
|
+
class="shadow-lg"
|
|
51
|
+
(closed)="closes.set(closes() + 1)"
|
|
52
|
+
>
|
|
53
|
+
<ui-nav-drawer-header class="gap-3">
|
|
54
|
+
<text>Menu</text>
|
|
55
|
+
</ui-nav-drawer-header>
|
|
56
|
+
<ui-nav-drawer-content class="pt-2">
|
|
57
|
+
<ui-nav-drawer-item
|
|
58
|
+
[active]="active() === 'home'"
|
|
59
|
+
(pressed)="presses.set(presses() + 1)"
|
|
60
|
+
>
|
|
61
|
+
<text class="text-foreground">Home</text>
|
|
62
|
+
</ui-nav-drawer-item>
|
|
63
|
+
<!-- A group: items nested in other elements count too. -->
|
|
64
|
+
<view class="group">
|
|
65
|
+
<ui-nav-drawer-item #settings [active]="active() === 'settings'">
|
|
66
|
+
<text class="text-foreground">Settings</text>
|
|
67
|
+
</ui-nav-drawer-item>
|
|
68
|
+
</view>
|
|
69
|
+
<ui-nav-drawer-item
|
|
70
|
+
[variant]="deleteVariant()"
|
|
71
|
+
[autoClose]="false"
|
|
72
|
+
class="mt-4"
|
|
73
|
+
>
|
|
74
|
+
<ui-icon name="trash" size="sm" />
|
|
75
|
+
<text class="text-foreground">Delete account</text>
|
|
76
|
+
</ui-nav-drawer-item>
|
|
77
|
+
</ui-nav-drawer-content>
|
|
78
|
+
<ui-nav-drawer-footer class="gap-4">
|
|
79
|
+
<text>Version 1</text>
|
|
80
|
+
</ui-nav-drawer-footer>
|
|
81
|
+
</ui-nav-drawer>
|
|
82
|
+
}
|
|
83
|
+
<!-- What the trash icon looks like plain, and in the destructive color. -->
|
|
84
|
+
<ui-icon name="trash" size="sm" class="plain-trash" />
|
|
85
|
+
<ui-icon
|
|
86
|
+
name="trash"
|
|
87
|
+
size="sm"
|
|
88
|
+
color="var(--destructive)"
|
|
89
|
+
class="destructive-trash"
|
|
90
|
+
/>
|
|
36
91
|
`,
|
|
37
92
|
})
|
|
38
93
|
class NavDrawerHost {
|
|
39
|
-
open =
|
|
94
|
+
readonly open = signal(false);
|
|
95
|
+
readonly side = signal<'left' | 'right'>('left');
|
|
96
|
+
readonly active = signal('settings');
|
|
97
|
+
readonly deleteVariant = signal<'default' | 'destructive'>('destructive');
|
|
98
|
+
// Takes the drawer out of the tree, as an `@if` or a route change would.
|
|
99
|
+
readonly shown = signal(true);
|
|
100
|
+
readonly closes = signal(0);
|
|
101
|
+
readonly presses = signal(0);
|
|
102
|
+
readonly drawer = viewChild.required(UiNavDrawer);
|
|
103
|
+
readonly settings = viewChild.required<UiNavDrawerItem>('settings');
|
|
40
104
|
}
|
|
41
105
|
|
|
106
|
+
const renderDrawer = async () => {
|
|
107
|
+
const result = await render(NavDrawerHost);
|
|
108
|
+
const { container, getByText } = result;
|
|
109
|
+
const panel = () =>
|
|
110
|
+
container.querySelector('[accessibility-exclusive-focus]')!;
|
|
111
|
+
return {
|
|
112
|
+
...result,
|
|
113
|
+
host: result.componentRef.instance as NavDrawerHost,
|
|
114
|
+
overlay: () => container.querySelector('overlay')!,
|
|
115
|
+
panel,
|
|
116
|
+
backdrop: () => panel().parentElement!,
|
|
117
|
+
scrollView: () => container.querySelector('scroll-view')!,
|
|
118
|
+
trigger: () => container.querySelector('[accessibility-element]')!,
|
|
119
|
+
// An item's own view, which holds its projected label.
|
|
120
|
+
item: (label: string) => getByText(label).parentElement!,
|
|
121
|
+
svg: (selector: string) => container.querySelector(`svg${selector}`)!,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Waits for the drawer's open or close sequence, which runs on timers, then
|
|
127
|
+
* renders what it changed.
|
|
128
|
+
*/
|
|
129
|
+
const after = async (ms: number) => {
|
|
130
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
131
|
+
await waitForUpdate();
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Long enough for the open sequence. The sequence is chained timers, and on
|
|
136
|
+
* Windows each can fire up to a clock tick (about 16ms) late, so 50ms was too
|
|
137
|
+
* short for the dialogs' sequences on CI's Windows runner.
|
|
138
|
+
*/
|
|
139
|
+
const OPENED = 150;
|
|
140
|
+
/** Long enough for the close animation and the hide after it, as above. */
|
|
141
|
+
const CLOSED = DURATION.normal + 150;
|
|
142
|
+
|
|
143
|
+
/** `__ElementAnimate`'s first argument for starting an animation. */
|
|
144
|
+
const START = 0;
|
|
145
|
+
/** `__ElementAnimate`'s first argument for canceling an animation. */
|
|
146
|
+
const CANCEL = 3;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The scale a pressable view's inline style holds, if any.
|
|
150
|
+
*/
|
|
151
|
+
const scaleOf = (view: Element) =>
|
|
152
|
+
(view as HTMLElement).style.getPropertyValue('transform');
|
|
153
|
+
|
|
42
154
|
describe('UiNavDrawer', () => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
inst.open.set(true);
|
|
48
|
-
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
49
|
-
inst.open.set(false);
|
|
50
|
-
await waitForUpdate();
|
|
51
|
-
// Destroyed during the close animation, as an `@if` around the drawer
|
|
52
|
-
// would do. Its close timer (DURATION.normal + 20) used to fire anyway
|
|
53
|
-
// and emit `closed` from the destroyed output.
|
|
54
|
-
unmount();
|
|
55
|
-
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
56
|
-
const warnings = warn.mock.calls.map(([message]) => String(message));
|
|
57
|
-
warn.mockRestore();
|
|
58
|
-
expect(warnings.filter((message) => message.includes('NG0953'))).toEqual(
|
|
59
|
-
[],
|
|
60
|
-
);
|
|
155
|
+
// Watched per test: the testing library resets the main thread after each.
|
|
156
|
+
let animate: MockInstance<LynxMainThread['__ElementAnimate']>;
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
animate = vi.spyOn(lynxMainThread(), '__ElementAnimate');
|
|
61
159
|
});
|
|
160
|
+
afterEach(() => animate.mockRestore());
|
|
62
161
|
|
|
63
|
-
it('
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
expect(
|
|
70
|
-
const invoke = vi.fn();
|
|
71
|
-
panel.invoke = invoke;
|
|
72
|
-
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
73
|
-
nativeElement: panel,
|
|
74
|
-
});
|
|
75
|
-
(inst as unknown as { panelRef: unknown }).panelRef = signal({
|
|
76
|
-
nativeElement: panel,
|
|
77
|
-
});
|
|
78
|
-
inst.open.set(true);
|
|
79
|
-
for (let i = 0; i < 5; i++) await waitForUpdate();
|
|
80
|
-
expect(invoke).toHaveBeenCalledWith('requestAccessibilityFocus');
|
|
81
|
-
expect(panel.getAttribute('accessibility-exclusive-focus')).toBe('true');
|
|
162
|
+
it('renders each part with its projected content', async () => {
|
|
163
|
+
const { getByText, trigger } = await renderDrawer();
|
|
164
|
+
expect(getByText('Menu')).toBeTruthy();
|
|
165
|
+
expect(getByText('Home')).toBeTruthy();
|
|
166
|
+
expect(getByText('Settings')).toBeTruthy();
|
|
167
|
+
expect(getByText('Version 1')).toBeTruthy();
|
|
168
|
+
expect(trigger().querySelector('svg')).toBeTruthy();
|
|
82
169
|
});
|
|
83
170
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
171
|
+
it("merges each part's class with its own, the given class winning", async () => {
|
|
172
|
+
const { container, panel, scrollView, item, trigger } =
|
|
173
|
+
await renderDrawer();
|
|
174
|
+
expect(panel().classList).toContain('bg-background');
|
|
175
|
+
expect(panel().classList).toContain('shadow-lg');
|
|
176
|
+
expect(scrollView().classList).toContain('h-full');
|
|
177
|
+
expect(scrollView().classList).toContain('pt-2');
|
|
178
|
+
expect(item('Delete account').classList).toContain('mt-4');
|
|
179
|
+
expect(trigger().classList).toContain('h-10');
|
|
180
|
+
expect(trigger().classList).toContain('ml-2');
|
|
181
|
+
// Each part's own view, not its host element, which keeps the bare class.
|
|
182
|
+
const header = container.querySelector('.border-b');
|
|
183
|
+
expect(header?.classList).toContain('gap-3');
|
|
184
|
+
expect(header?.classList).not.toContain('gap-1.5');
|
|
185
|
+
const footer = container.querySelector('.border-t');
|
|
186
|
+
expect(footer?.classList).toContain('gap-4');
|
|
187
|
+
expect(footer?.classList).not.toContain('gap-2');
|
|
96
188
|
});
|
|
97
189
|
|
|
98
|
-
it('
|
|
99
|
-
const {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
(
|
|
104
|
-
|
|
190
|
+
it('stays hidden until it is opened', async () => {
|
|
191
|
+
const { host, overlay, panel } = await renderDrawer();
|
|
192
|
+
await after(CLOSED);
|
|
193
|
+
expect(overlay().getAttribute('style')).toContain('display: none');
|
|
194
|
+
expect(panel().getAttribute('accessibility-exclusive-focus')).toBe('false');
|
|
195
|
+
expect(animate).not.toHaveBeenCalled();
|
|
196
|
+
// Starting closed isn't closing: `closed` waits for a real close.
|
|
197
|
+
expect(host.closes()).toBe(0);
|
|
105
198
|
});
|
|
106
199
|
|
|
107
|
-
it('
|
|
108
|
-
const {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(
|
|
200
|
+
it('opens when the trigger is tapped', async () => {
|
|
201
|
+
const { host, trigger, overlay } = await renderDrawer();
|
|
202
|
+
fireEvent.tap(trigger());
|
|
203
|
+
await after(OPENED);
|
|
204
|
+
expect(host.open()).toBe(true);
|
|
205
|
+
expect(overlay().getAttribute('style')).not.toContain('display: none');
|
|
113
206
|
});
|
|
114
207
|
|
|
115
|
-
it('
|
|
116
|
-
const {
|
|
117
|
-
|
|
118
|
-
expect(()
|
|
119
|
-
|
|
120
|
-
|
|
208
|
+
it('slides in from the left and back out', async () => {
|
|
209
|
+
const { host, overlay, backdrop, panel } = await renderDrawer();
|
|
210
|
+
expect(panel().getAttribute('style')).toContain('left: 0');
|
|
211
|
+
expect(panel().classList).toContain('border-r');
|
|
212
|
+
|
|
213
|
+
host.open.set(true);
|
|
214
|
+
await after(OPENED);
|
|
215
|
+
expect(animate).toHaveBeenCalledWith(backdrop(), [
|
|
216
|
+
START,
|
|
217
|
+
expect.any(String),
|
|
218
|
+
[{ opacity: 0 }, { opacity: 1 }],
|
|
219
|
+
expect.objectContaining({ duration: DURATION.normal }),
|
|
220
|
+
]);
|
|
221
|
+
expect(animate).toHaveBeenCalledWith(panel(), [
|
|
222
|
+
START,
|
|
223
|
+
expect.any(String),
|
|
224
|
+
[
|
|
225
|
+
{ transform: 'translateX(-100%)', opacity: 0.8 },
|
|
226
|
+
{ transform: 'translateX(0%)', opacity: 1 },
|
|
227
|
+
],
|
|
228
|
+
expect.objectContaining({ duration: DURATION.slow }),
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
host.open.set(false);
|
|
232
|
+
await after(CLOSED);
|
|
233
|
+
expect(animate).toHaveBeenCalledWith(backdrop(), [
|
|
234
|
+
START,
|
|
235
|
+
expect.any(String),
|
|
236
|
+
[{ opacity: 1 }, { opacity: 0 }],
|
|
237
|
+
expect.any(Object),
|
|
238
|
+
]);
|
|
239
|
+
expect(animate).toHaveBeenCalledWith(panel(), [
|
|
240
|
+
START,
|
|
241
|
+
expect.any(String),
|
|
242
|
+
[
|
|
243
|
+
{ transform: 'translateX(0%)', opacity: 1 },
|
|
244
|
+
{ transform: 'translateX(-100%)', opacity: 0.8 },
|
|
245
|
+
],
|
|
246
|
+
expect.any(Object),
|
|
247
|
+
]);
|
|
248
|
+
expect(overlay().getAttribute('style')).toContain('display: none');
|
|
249
|
+
expect(host.closes()).toBe(1);
|
|
121
250
|
});
|
|
122
251
|
|
|
123
|
-
it('
|
|
124
|
-
const {
|
|
125
|
-
|
|
126
|
-
const view = container.querySelector('view') as Element;
|
|
127
|
-
patchRefs(inst, view);
|
|
128
|
-
inst.open.set(true);
|
|
129
|
-
await waitForUpdate();
|
|
130
|
-
await waitForUpdate();
|
|
252
|
+
it('slides in from the right and back out', async () => {
|
|
253
|
+
const { host, panel } = await renderDrawer();
|
|
254
|
+
host.side.set('right');
|
|
131
255
|
await waitForUpdate();
|
|
256
|
+
expect(panel().getAttribute('style')).toContain('right: 0');
|
|
257
|
+
expect(panel().classList).toContain('border-l');
|
|
132
258
|
|
|
133
|
-
|
|
134
|
-
await
|
|
135
|
-
|
|
136
|
-
|
|
259
|
+
host.open.set(true);
|
|
260
|
+
await after(OPENED);
|
|
261
|
+
expect(animate).toHaveBeenCalledWith(panel(), [
|
|
262
|
+
START,
|
|
263
|
+
expect.any(String),
|
|
264
|
+
[
|
|
265
|
+
{ transform: 'translateX(100%)', opacity: 0.8 },
|
|
266
|
+
{ transform: 'translateX(0%)', opacity: 1 },
|
|
267
|
+
],
|
|
268
|
+
expect.any(Object),
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
host.open.set(false);
|
|
272
|
+
await after(CLOSED);
|
|
273
|
+
expect(animate).toHaveBeenCalledWith(panel(), [
|
|
274
|
+
START,
|
|
275
|
+
expect.any(String),
|
|
276
|
+
[
|
|
277
|
+
{ transform: 'translateX(0%)', opacity: 1 },
|
|
278
|
+
{ transform: 'translateX(100%)', opacity: 0.8 },
|
|
279
|
+
],
|
|
280
|
+
expect.any(Object),
|
|
281
|
+
]);
|
|
137
282
|
});
|
|
138
283
|
|
|
139
|
-
it('
|
|
140
|
-
const {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
await
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
),
|
|
157
|
-
).toBe(true);
|
|
284
|
+
it('cancels the previous animations when it opens again', async () => {
|
|
285
|
+
const { host, backdrop, panel } = await renderDrawer();
|
|
286
|
+
host.open.set(true);
|
|
287
|
+
await after(OPENED);
|
|
288
|
+
host.open.set(false);
|
|
289
|
+
await after(CLOSED);
|
|
290
|
+
animate.mockClear();
|
|
291
|
+
|
|
292
|
+
host.open.set(true);
|
|
293
|
+
await after(OPENED);
|
|
294
|
+
// The close animations hold their last frame (fill: forwards) until
|
|
295
|
+
// they're canceled, which would hide the panel being opened.
|
|
296
|
+
expect(animate).toHaveBeenCalledWith(backdrop(), [
|
|
297
|
+
CANCEL,
|
|
298
|
+
expect.any(String),
|
|
299
|
+
]);
|
|
300
|
+
expect(animate).toHaveBeenCalledWith(panel(), [CANCEL, expect.any(String)]);
|
|
158
301
|
});
|
|
159
302
|
|
|
160
|
-
it('
|
|
161
|
-
const {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
303
|
+
it('moves a screen reader into the panel and keeps it there while open', async () => {
|
|
304
|
+
const { host, panel } = await renderDrawer();
|
|
305
|
+
const invoke = vi.spyOn(host.drawer().panelRef().nativeElement, 'invoke');
|
|
306
|
+
host.open.set(true);
|
|
307
|
+
await after(OPENED);
|
|
308
|
+
expect(invoke).toHaveBeenCalledWith('requestAccessibilityFocus');
|
|
309
|
+
expect(panel().getAttribute('accessibility-exclusive-focus')).toBe('true');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('closes when the backdrop is tapped', async () => {
|
|
313
|
+
const { host, backdrop } = await renderDrawer();
|
|
314
|
+
host.open.set(true);
|
|
315
|
+
await after(OPENED);
|
|
316
|
+
fireEvent.tap(backdrop());
|
|
317
|
+
expect(host.open()).toBe(false);
|
|
318
|
+
await after(CLOSED);
|
|
319
|
+
expect(host.closes()).toBe(1);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('stays open when the panel is tapped', async () => {
|
|
323
|
+
const { host, panel } = await renderDrawer();
|
|
324
|
+
host.open.set(true);
|
|
325
|
+
await after(OPENED);
|
|
326
|
+
// The panel catches its taps, so they don't reach the backdrop.
|
|
327
|
+
fireEvent.tap(panel(), { eventType: 'catchEvent' });
|
|
169
328
|
await waitForUpdate();
|
|
170
|
-
|
|
171
|
-
// component is still alive. Destroying it clears the sequence's timers.
|
|
172
|
-
await new Promise((r) => setTimeout(r, DURATION.normal + 50));
|
|
173
|
-
expect(inst.open()).toBe(false);
|
|
174
|
-
expect(closed).toHaveBeenCalledOnce();
|
|
329
|
+
expect(host.open()).toBe(true);
|
|
175
330
|
});
|
|
176
|
-
});
|
|
177
331
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
332
|
+
it('stops its close sequence when it is destroyed while closing', async () => {
|
|
333
|
+
const warn = vi.spyOn(console, 'warn');
|
|
334
|
+
const { host } = await renderDrawer();
|
|
335
|
+
host.open.set(true);
|
|
336
|
+
await after(OPENED);
|
|
337
|
+
host.open.set(false);
|
|
338
|
+
await after(0);
|
|
339
|
+
// Destroyed during the close animation. Its close timer
|
|
340
|
+
// (DURATION.normal + 20) used to fire anyway and emit `closed` from the
|
|
341
|
+
// destroyed output, which Angular warns about in dev builds.
|
|
342
|
+
host.shown.set(false);
|
|
343
|
+
await waitForUpdate();
|
|
344
|
+
await after(CLOSED);
|
|
345
|
+
const warnings = warn.mock.calls.map(([message]) => String(message));
|
|
346
|
+
warn.mockRestore();
|
|
347
|
+
expect(warnings.filter((message) => message.includes('NG0953'))).toEqual(
|
|
348
|
+
[],
|
|
349
|
+
);
|
|
350
|
+
expect(host.closes()).toBe(0);
|
|
184
351
|
});
|
|
185
352
|
});
|
|
186
353
|
|
|
187
354
|
describe('UiNavDrawerContent', () => {
|
|
188
|
-
it('
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
355
|
+
it('scrolls the active item into view when the drawer opens', async () => {
|
|
356
|
+
const { host } = await renderDrawer();
|
|
357
|
+
const scroll = vi.spyOn(
|
|
358
|
+
host.settings().containerRef()!.nativeElement,
|
|
359
|
+
'invoke',
|
|
360
|
+
);
|
|
361
|
+
host.open.set(true);
|
|
362
|
+
await after(OPENED);
|
|
363
|
+
// Instantly and centered, so the drawer slides in already scrolled.
|
|
364
|
+
expect(scroll).toHaveBeenCalledWith(
|
|
365
|
+
'scrollIntoView',
|
|
366
|
+
expect.objectContaining({ block: 'center', behavior: 'auto' }),
|
|
367
|
+
);
|
|
195
368
|
});
|
|
196
369
|
|
|
197
|
-
it('
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
370
|
+
it('scrolls the active item into view once its content is laid out', async () => {
|
|
371
|
+
const { host, scrollView } = await renderDrawer();
|
|
372
|
+
host.open.set(true);
|
|
373
|
+
await after(OPENED);
|
|
374
|
+
const scroll = vi.spyOn(
|
|
375
|
+
host.settings().containerRef()!.nativeElement,
|
|
376
|
+
'invoke',
|
|
377
|
+
);
|
|
378
|
+
fireEvent.contentsizechanged(scrollView());
|
|
379
|
+
expect(scroll).toHaveBeenCalledWith('scrollIntoView', expect.any(Object));
|
|
380
|
+
});
|
|
202
381
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
382
|
+
it("doesn't scroll while the drawer is closed", async () => {
|
|
383
|
+
const { host, scrollView } = await renderDrawer();
|
|
384
|
+
const scroll = vi.spyOn(
|
|
385
|
+
host.settings().containerRef()!.nativeElement,
|
|
386
|
+
'invoke',
|
|
387
|
+
);
|
|
388
|
+
// Content collapsing as the overlay hides relays out too.
|
|
389
|
+
fireEvent.contentsizechanged(scrollView());
|
|
390
|
+
await after(OPENED);
|
|
391
|
+
expect(scroll).not.toHaveBeenCalled();
|
|
209
392
|
});
|
|
210
393
|
|
|
211
|
-
it('
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
).not.
|
|
394
|
+
it("doesn't scroll when no item is active", async () => {
|
|
395
|
+
const { host } = await renderDrawer();
|
|
396
|
+
host.active.set('');
|
|
397
|
+
const scroll = vi.spyOn(
|
|
398
|
+
host.settings().containerRef()!.nativeElement,
|
|
399
|
+
'invoke',
|
|
400
|
+
);
|
|
401
|
+
host.open.set(true);
|
|
402
|
+
await after(OPENED);
|
|
403
|
+
expect(scroll).not.toHaveBeenCalled();
|
|
221
404
|
});
|
|
222
405
|
});
|
|
223
406
|
|
|
224
407
|
describe('UiNavDrawerItem', () => {
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
await waitForUpdate();
|
|
408
|
+
it('marks the active item with the accent background', async () => {
|
|
409
|
+
const { host, item } = await renderDrawer();
|
|
410
|
+
expect(item('Settings').classList).toContain('bg-accent');
|
|
411
|
+
expect(item('Home').classList).toContain('bg-transparent');
|
|
230
412
|
|
|
231
|
-
|
|
232
|
-
providers: [{ provide: UiNavDrawer, useValue: parent }],
|
|
233
|
-
});
|
|
234
|
-
const item = itemResult.componentRef.instance as UiNavDrawerItem;
|
|
235
|
-
setInputSignal(item.autoClose, autoClose);
|
|
413
|
+
host.active.set('home');
|
|
236
414
|
await waitForUpdate();
|
|
237
|
-
|
|
238
|
-
|
|
415
|
+
expect(item('Home').classList).toContain('bg-accent');
|
|
416
|
+
expect(item('Settings').classList).toContain('bg-transparent');
|
|
417
|
+
});
|
|
239
418
|
|
|
240
|
-
it('
|
|
241
|
-
const {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(item
|
|
245
|
-
expect(
|
|
246
|
-
expect(
|
|
419
|
+
it('emits pressed and closes the drawer when tapped', async () => {
|
|
420
|
+
const { host, item } = await renderDrawer();
|
|
421
|
+
host.open.set(true);
|
|
422
|
+
await waitForUpdate();
|
|
423
|
+
fireEvent.tap(item('Home'));
|
|
424
|
+
expect(host.presses()).toBe(1);
|
|
425
|
+
expect(host.open()).toBe(false);
|
|
247
426
|
});
|
|
248
427
|
|
|
249
|
-
it('
|
|
250
|
-
const {
|
|
251
|
-
|
|
252
|
-
|
|
428
|
+
it('leaves the drawer open when tapped without autoClose', async () => {
|
|
429
|
+
const { host, item } = await renderDrawer();
|
|
430
|
+
host.open.set(true);
|
|
431
|
+
await waitForUpdate();
|
|
432
|
+
fireEvent.tap(item('Delete account'));
|
|
433
|
+
expect(host.open()).toBe(true);
|
|
253
434
|
});
|
|
254
435
|
|
|
255
|
-
it('
|
|
256
|
-
const { item } = await
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
expect(()
|
|
263
|
-
|
|
264
|
-
|
|
436
|
+
it('scales down while pressed, and back on release', async () => {
|
|
437
|
+
const { item } = await renderDrawer();
|
|
438
|
+
const view = item('Home');
|
|
439
|
+
|
|
440
|
+
fireEvent.touchstart(view);
|
|
441
|
+
expect(scaleOf(view)).toBe('scale(0.985)');
|
|
442
|
+
fireEvent.touchend(view);
|
|
443
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
444
|
+
|
|
445
|
+
// A scroll that takes the touch over cancels it.
|
|
446
|
+
fireEvent.touchstart(view);
|
|
447
|
+
fireEvent.touchcancel(view);
|
|
448
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
449
|
+
|
|
450
|
+
// The mouse, on the web.
|
|
451
|
+
fireEvent.mousedown(view);
|
|
452
|
+
expect(scaleOf(view)).toBe('scale(0.985)');
|
|
453
|
+
fireEvent.mouseup(view);
|
|
454
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
455
|
+
fireEvent.mousedown(view);
|
|
456
|
+
fireLynxEvent(view, 'mouseleave');
|
|
457
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
265
458
|
});
|
|
266
459
|
|
|
267
|
-
it(
|
|
268
|
-
const { item,
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
await waitForUpdate();
|
|
273
|
-
expect(view()?.getAttribute('class')).toContain(
|
|
460
|
+
it('turns its label and icons destructive', async () => {
|
|
461
|
+
const { host, item, svg } = await renderDrawer();
|
|
462
|
+
const icon = () => item('Delete account').querySelector('svg')!;
|
|
463
|
+
// Labels styled text-foreground follow the redirected variable.
|
|
464
|
+
expect(item('Delete account').classList).toContain(
|
|
274
465
|
'[--foreground:var(--destructive)]',
|
|
275
466
|
);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const { item, itemResult } = await renderItem();
|
|
280
|
-
const iconColor = itemResult.componentRef.injector.get(UI_ICON_COLOR);
|
|
281
|
-
expect(iconColor()).toBeUndefined();
|
|
282
|
-
setInputSignal(item.variant, 'destructive');
|
|
283
|
-
expect(iconColor()).toBe('var(--destructive)');
|
|
284
|
-
});
|
|
467
|
+
expect(icon().getAttribute('content')).toBe(
|
|
468
|
+
svg('.destructive-trash').getAttribute('content'),
|
|
469
|
+
);
|
|
285
470
|
|
|
286
|
-
|
|
287
|
-
const { item, itemResult } = await renderItem();
|
|
288
|
-
setInputSignal(item.active, true);
|
|
471
|
+
host.deleteVariant.set('default');
|
|
289
472
|
await waitForUpdate();
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const invoke = vi.fn();
|
|
297
|
-
(item as unknown as { containerRef: unknown }).containerRef = signal({
|
|
298
|
-
nativeElement: { invoke },
|
|
299
|
-
});
|
|
300
|
-
item.scrollIntoView();
|
|
301
|
-
expect(invoke).toHaveBeenCalledWith('scrollIntoView', expect.any(Object));
|
|
302
|
-
|
|
303
|
-
// No-container path: containerRef is undefined so `?.` short-circuits.
|
|
304
|
-
(item as unknown as { containerRef: () => undefined }).containerRef = () =>
|
|
305
|
-
undefined;
|
|
306
|
-
expect(() => item.scrollIntoView()).not.toThrow();
|
|
307
|
-
void itemResult;
|
|
473
|
+
expect(item('Delete account').classList).not.toContain(
|
|
474
|
+
'[--foreground:var(--destructive)]',
|
|
475
|
+
);
|
|
476
|
+
expect(icon().getAttribute('content')).toBe(
|
|
477
|
+
svg('.plain-trash').getAttribute('content'),
|
|
478
|
+
);
|
|
308
479
|
});
|
|
309
480
|
});
|
|
310
481
|
|
|
311
482
|
describe('UiNavDrawerTrigger', () => {
|
|
312
483
|
it('is a labeled button for screen readers', async () => {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const { container } = await render(UiNavDrawerTrigger);
|
|
317
|
-
const trigger = container.querySelector('[accessibility-element]');
|
|
318
|
-
expect(trigger?.getAttribute('accessibility-traits')).toBe('button');
|
|
319
|
-
expect(trigger?.getAttribute('accessibility-label')).toBe(
|
|
320
|
-
'Open navigation menu',
|
|
484
|
+
const { trigger, getByRole } = await renderDrawer();
|
|
485
|
+
expect(getByRole('button', { name: 'Open navigation menu' })).toBe(
|
|
486
|
+
trigger(),
|
|
321
487
|
);
|
|
322
488
|
});
|
|
323
489
|
|
|
324
|
-
it('
|
|
325
|
-
const {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
490
|
+
it('scales down while pressed, and back on release', async () => {
|
|
491
|
+
const { trigger } = await renderDrawer();
|
|
492
|
+
const view = trigger();
|
|
493
|
+
|
|
494
|
+
fireEvent.touchstart(view);
|
|
495
|
+
expect(scaleOf(view)).toBe('scale(0.97)');
|
|
496
|
+
fireEvent.touchend(view);
|
|
497
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
498
|
+
|
|
499
|
+
fireEvent.touchstart(view);
|
|
500
|
+
fireEvent.touchcancel(view);
|
|
501
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
334
502
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
expect(() => inst.onPressStart()).not.toThrow();
|
|
343
|
-
expect(() => inst.onPressEnd()).not.toThrow();
|
|
344
|
-
expect(() => inst.onPressCancel()).not.toThrow();
|
|
503
|
+
fireEvent.mousedown(view);
|
|
504
|
+
expect(scaleOf(view)).toBe('scale(0.97)');
|
|
505
|
+
fireEvent.mouseup(view);
|
|
506
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
507
|
+
fireEvent.mousedown(view);
|
|
508
|
+
fireLynxEvent(view, 'mouseleave');
|
|
509
|
+
expect(scaleOf(view)).toBe('scale(1)');
|
|
345
510
|
});
|
|
346
511
|
});
|