-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathSelectField.cascade.test.tsx
More file actions
138 lines (128 loc) · 4.32 KB
/
Copy pathSelectField.cascade.test.tsx
File metadata and controls
138 lines (128 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* ObjectUI
* Copyright (c) 2024-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Cascading / role-gated `select` options (#2284).
*
* Exercises the observable, non-Radix behaviour of {@link SelectField}: the
* dependency gate (a "select the parent first" empty-state) and the cascade
* clear (a value dropped when the offered set no longer includes it). The
* per-option filtering itself is unit-tested in `@object-ui/core`
* (`optionRules.test.ts`); here we prove the widget wires `dependentValues` +
* predicate scope into that resolver.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PredicateScopeProvider } from '@object-ui/react';
import { SelectField } from './SelectField';
const provinceField = {
name: 'province',
type: 'select',
dependsOn: 'country',
options: [
{ label: 'Zhejiang', value: 'zj', visibleWhen: "record.country == 'cn'" },
{ label: 'California', value: 'ca', visibleWhen: "record.country == 'us'" },
],
} as any;
beforeEach(() => {
vi.clearAllMocks();
});
describe('SelectField — dependency gating (#2284)', () => {
it('gates with a "select parent first" hint while the controlling field is empty', () => {
render(
<SelectField
value={undefined}
onChange={vi.fn()}
field={provinceField}
{...({ name: 'province', dependentValues: {} } as any)}
/>,
);
const gate = screen.getByTestId('select-empty-province');
expect(gate).toHaveTextContent(/select country first/i);
// Gated → no live combobox is offered.
expect(screen.queryByRole('combobox')).not.toBeInTheDocument();
});
it('unlocks the combobox once the controlling field is set', () => {
render(
<SelectField
value={undefined}
onChange={vi.fn()}
field={provinceField}
{...({ name: 'province', dependentValues: { country: 'cn' } } as any)}
/>,
);
expect(screen.queryByTestId('select-empty-province')).not.toBeInTheDocument();
expect(screen.getByRole('combobox')).toBeInTheDocument();
});
});
describe('SelectField — cascade clear (#2284)', () => {
it('clears a value the parent change no longer offers', () => {
const onChange = vi.fn();
render(
<SelectField
value={'ca'}
onChange={onChange}
field={provinceField}
{...({ name: 'province', dependentValues: { country: 'cn' } } as any)}
/>,
);
// 'ca' is a US province — under country=cn it is not offered, so it is dropped.
expect(onChange).toHaveBeenCalledWith(undefined);
});
it('keeps a value that is still offered', () => {
const onChange = vi.fn();
render(
<SelectField
value={'zj'}
onChange={onChange}
field={provinceField}
{...({ name: 'province', dependentValues: { country: 'cn' } } as any)}
/>,
);
expect(onChange).not.toHaveBeenCalled();
});
});
describe('SelectField — role / context gating (#2284)', () => {
const tierField = {
name: 'tier',
type: 'select',
options: [
{ label: 'Standard', value: 'standard' },
{ label: 'Admin only', value: 'admin_only', visibleWhen: "'admin' in current_user.positions" },
],
} as any;
it('clears an admin-only value for a non-admin (offered set excludes it)', () => {
const onChange = vi.fn();
render(
<PredicateScopeProvider scope={{ current_user: { positions: ['sales'] } }}>
<SelectField
value={'admin_only'}
onChange={onChange}
field={tierField}
{...({ name: 'tier', dependentValues: {} } as any)}
/>
</PredicateScopeProvider>,
);
expect(onChange).toHaveBeenCalledWith(undefined);
});
it('keeps an admin-only value for an admin', () => {
const onChange = vi.fn();
render(
<PredicateScopeProvider scope={{ current_user: { positions: ['admin'] } }}>
<SelectField
value={'admin_only'}
onChange={onChange}
field={tierField}
{...({ name: 'tier', dependentValues: {} } as any)}
/>
</PredicateScopeProvider>,
);
expect(onChange).not.toHaveBeenCalled();
});
});