Skip to content

Commit bc97f92

Browse files
fix(app-shell): an interface page relays its source view's hiddenFields and fieldOrder (objectui#10638) (#10667)
Fixes #10638 Clause-②: no — an interface page begins honouring two declared per-view keys it drops today; no export, schema or accept set moves. Executes triage `5833520187` (bug · p2) under claim `5833614033` (`domain:ui` seat 2, session `session_014mXUNuFomfj24w7s1pZzhN`). This PR puts the source view's `hiddenFields` and `fieldOrder` into the list schema `InterfaceListPage` hands `ListView`. It states the page config's precedence and mirrors the objectui#7516 relay pin. ## What changed - **The relay** (`packages/app-shell/src/views/InterfaceListPage.tsx`). The schema literal now gets two conditional spreads beside `columns`: `hiddenFields: view.hiddenFields` and `fieldOrder: view.fieldOrder`. Each is present only when the view authored the key and the view supplies the columns. The page composes nothing. `ListView`'s `effectiveFields` memo already runs `columns` projects → `hiddenFields` subtracts → `fieldOrder` sorts the survivors, and an unlisted survivor sorts last. - **The pin** (`InterfaceListPage.viewFieldKeysRelay-10638.test.tsx`). `ListView` is stubbed and its `schema` prop is captured, the same posture as `ObjectView.fieldOrderRelay-7516.test.tsx`. Three FIX cases: the view's `hiddenFields` arrives verbatim; the view's `fieldOrder` arrives verbatim; the whole composition arrives together. One PRECEDENCE case and two CONTROLs: an empty view `columns`, and a view that authors neither key. - **The file header** (rounds 2 and 3). It now states `sourceView` as a fallback: the view's columns (with its `hiddenFields` / `fieldOrder`) and sort are inherited unless the page defines its own `columns` / `sort`, and its base filter is always inherited with the page's `filterBy` appended (ADR-0047 revised). The "never restated" wording it replaces was false on base as well. - **Changeset** `.changeset/10638-interface-page-view-field-keys.md`, `'@object-ui/app-shell': patch`. ## The precedence (H2): the whole composition, from one source `cfg` has no spelling of either key. `InterfacePageConfigSchema` is a `strictObject`, and its alias map sends `fields` / `columnList` to `columns` and neither key anywhere. So the two keys resolve **with** the page's column list, as one unit, on the same three branches `columns` already used: 1. **The page authors its own `columns`.** That list is used as it stands, and neither view key applies. Released spec (`@objectstack/spec` 17.4.0): page `columns` are "Defined directly on the page (no view inheritance)", and `sourceView` is "Still honored at runtime as a fallback when the page has no own `columns`". 2. **Otherwise the view's non-empty `columns`.** The view's `hiddenFields` and `fieldOrder` come with them. 3. **Otherwise object-derived defaults.** Neither key applies. `ListViewSchema.columns` says an empty list "declares no projection, so neither of them applies". This is spec source at objectstack `origin/main` (objectstack#19598, `9dcdb77`), not yet in the released 17.4.0. Why not "the page's `columns` projects, the view's keys subtract and sort", as the order's H2 sketched? The composition is declared **per view**. The `ListViewSchema` composition docblock says "Three keys on this schema together build one field list"; this is spec source at objectstack `origin/main` (objectstack#19598), not yet in the released 17.4.0. Mixing sources would also break design mode. The design-mode column drag (`onColumnStateChange`) saves the order it shows as the page's `columns`. A relayed view `fieldOrder` would then sort that list straight back, so the drag would appear to do nothing. Ablation B below shows the pin catches that wiring. ## Premises, measured on base `4a3d500fd` before the first edit - **H1 holds.** The site is as the card describes: `resolveSourceView` feeds a schema literal with 0 mentions of either key. Fixture: `columns: ['name','owner','stage']`, `hiddenFields: ['owner']`, `fieldOrder: ['stage','name']`. - What `ListView` **receives** on base: the pin reads `Tests 3 failed | 3 passed (6)`, `expected undefined to deeply equal [ 'owner' ]` and `… [ 'stage', 'name' ]`. `schema.columns` arrives as `['name','owner','stage']`. - What it **draws**, from a one-off probe that was not committed: the real `InterfaceListPage` and the real `ListView`, with only `object-grid` stubbed to record the column list it is handed. Base draws `["name","owner","stage"]`. Head draws `["stage","name"]`. - **H2.** The precedence is set out above. The PRECEDENCE case is green on base by construction, because base relays neither key. What it catches is an over-broad relay; see ablation B. - **H3.** The relay only delivers the values; `ListView` is untouched. - **H4.** The pin goes red on base and green on head, with per-hunk ablations below. - **H5.** No sentence needed reconciling: - No interface-page doc and no `packages/app-shell/README.md` sentence names which source-view keys the page honours. - Of the pending changesets naming `InterfaceListPage`, only `.changeset/7218-rowcolor-host-relay.md` names the edited file (per `check:changeset-claims`). Its paragraph ("has shipped `rowColor: view.rowColor` next to `grouping` and `pagination`") was re-read and is still true. ## Verification of the change, round 1, on `b172bf0fb` All runs went through the shared verify lock. Legs 2–4 ran in one hold, each with a trap-protected restore proven by the blob hash matching HEAD (`00977917bcae`) and `git diff HEAD` empty. - **Head:** pin `Tests 6 passed (6)`. Probe draws `["stage","name"]`. - **Base file swapped in** (`git show 4a3d500:…`; on disk the `viewComposes` count is 0 and the blob equals the base blob `f3e2e80e0509`): pin `Tests 3 failed | 3 passed (6)`, the three FIX cases. Probe draws `["name","owner","stage"]`. - **Ablation A:** the relay hunk deleted, both spread lines. Tool: `ablation-replace.mjs` (objectstack), anchor `x1 → x0`, blob `00977917bcae → 740fff063f76`; an in-run `grep -c` read `0` and `0`. Predicted: three FIX cases red. Measured: `Tests 3 failed | 3 passed (6)`, the three FIX cases. The probe draws `["name","owner","stage"]`. - **Ablation B:** the precedence gate removed (`viewComposes && view.` → `true && view.`, `--expect 2`, anchor `x2 → x0`, replacement `x0 → x2`), so the keys are carried unconditionally. Predicted: PRECEDENCE and the empty-`columns` CONTROL red. Measured: `Tests 2 failed | 4 passed (6)`, exactly those two. - **Dependency closure:** `turbo run build --filter='@object-ui/app-shell^...' --concurrency=2`. `Tasks: 28 successful, 28 total`. - **`@object-ui/app-shell` type-check:** `tsc --noEmit && tsc -p tsconfig.test.json`, exit 0. `--listFilesOnly` on the test project lists the new pin. - **Tests that name `InterfaceListPage`** (23 files across 9 packages): `Test Files 23 passed (23)`, `Tests 362 passed (362)`. - **Lint, narrowed and proven:** - Population from ESLint's own config: `isPathIgnored` is false for both changed `.ts`/`.tsx` files. - Count: 2 files, 0 errors. `InterfaceListPage.tsx` has 39 warnings on head and 39 on base. The pin has 9 `no-explicit-any` warnings, the same as its sibling `InterfaceListPage.mapConfig.test.tsx`. - Invariance: no type-aware linting (`parserOptions.project` / `projectService` unset), and no rule under `eslint-rules/` reads the filesystem, so this diff cannot move a verdict on any untouched file. - **Gates (lock-free), with each gate's own verdict line:** - `check-changeset-presence` ✅ `2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)` - `check:new-line-citations` `VERDICT new-cross-file-line-citations: 0 new citation(s)` - `check:control-bytes` ✅ OK - `check-changeset-no-major` ✅ - `check-changeset-fixed` ✅ - `check:pending-changeset-literals` ✅ - `check:vi-mock-specifiers` / `-inherit` / `-override-shape` ✅ OK - `check:test-path-roots` ✅ OK - `check:changeset-claims`: report-only; the one paragraph it names was re-read, as above - `check-governed-queue-guard --test` on the three paths: `NOT GOVERNED` ## Round 2, on `2a26e30ec` (wording only, no code change) The contract review passed the change and asked for three wording fixes: 1. **The file header clause** now states `sourceView` as the ADR-0047 (revised) fallback. The replaced text said "never restated", which was false on base too. 2. **The changeset** now reads "carried that view's `columns`, `filter`, `sort` (and its other keys)", so the list is no longer read as exhaustive. The frontmatter is byte-identical: md5 of its first three lines is `4e8ce55021fe3b14329c8b4cb3f9f354` before and after. 3. **This body** marks which spec quotes are released (17.4.0) and which are spec source at objectstack `origin/main` (objectstack#19598). Gates on `2a26e30ec`: - `check-changeset-presence` ✅ `2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)` - `check-changeset-no-major` ✅ - `check:control-bytes` ✅ OK - `check:new-line-citations` `VERDICT new-cross-file-line-citations: 0 new citation(s)` - `@object-ui/app-shell` type-check: exit 0, after the closure build (`Tasks: 28 successful, 28 total`, all cached) Merge check: fresh `origin/main` `ed8251189` has not touched the three files since base, and `git merge-tree` is clean, so no merge commit was needed. ## Round 3, on `106a48803` (one comment clause) The round-2 re-review found the header clause false on its filter leg: the page always spreads the view's `filter` and appends its own `filterBy` (`:437-440` at this head), so the page never displaces the base filter. The clause now says so. Comment only; `check:control-bytes` and `check:new-line-citations` pass, the app-shell type-check exits 0, and `git merge-tree` against `origin/main` `f905090a1` is clean. Not run locally; these are CI's: the full `pnpm test` farm, `Lint`, E2E, and the repo-wide `pnpm lint`. ## Acceptance notes - The page still takes the source view's `filter`, `sort`, `grouping`, `rowColor`, `pagination`, `userFilters` and the other keys whether or not it authors its own `columns`. This PR does not change that; it scopes only the two new keys. Read from source, not measured. - The object route (`ObjectView`) handles a `columns: []` view differently. It fills in default columns, then relays that view's `hiddenFields` / `fieldOrder`, so the keys subtract from and sort the defaults there. This page applies neither key to its defaults. Carded by the seat as objectui#10694. --- _Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 69a6fc1 commit bc97f92

3 files changed

Lines changed: 266 additions & 3 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(app-shell): an interface page's source view now hides and orders its columns
6+
7+
An ADR-0047 interface list page (`InterfaceListPage`) builds its list schema
8+
from the view its `interfaceConfig.sourceView` names, and carried that view's
9+
`columns`, `filter`, `sort` (and its other keys) but not its `hiddenFields` or
10+
`fieldOrder`. A
11+
source view that authored either still showed every column of its `columns`, in
12+
`columns` order: accepted, served, then dropped at the page. Both keys now reach
13+
`ListView` beside the view's `columns`, which composes them as it always has —
14+
`columns` projects, `hiddenFields` subtracts, `fieldOrder` sorts what survives,
15+
and a surviving column it does not list sorts last (objectstack#15184).
16+
17+
The two keys travel with the view's column list, as one unit:
18+
19+
- A page that defines its own `columns` uses that list as it stands, and neither
20+
view key applies. The page config has no spelling of either key, and its own
21+
`columns` are not inherited from any view. This also keeps a design-mode column
22+
drag in place: the drag saves the order it shows as the page's `columns`, and a
23+
view `fieldOrder` would otherwise sort it back.
24+
- A view whose `columns` is empty declares no projection, so the page derives
25+
the object's default columns and neither key applies.

‎packages/app-shell/src/views/InterfaceListPage.tsx‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
* object's list views as switcher tabs and lets users create views, this
77
* surface is deliberately closed:
88
*
9-
* • the page REFERENCES one view (`interfaceConfig.sourceView`) — columns,
10-
* base filter and sort are inherited, never restated (the iron rule);
9+
* • the page REFERENCES one view (`interfaceConfig.sourceView`) as a
10+
* fallback — its columns (with its `hiddenFields` / `fieldOrder`) and
11+
* sort are inherited unless the page defines its own `columns` /
12+
* `sort`; its base filter is always inherited, with the page's
13+
* `filterBy` appended (ADR-0047 revised);
1114
* • end users get exactly the `userFilters` the author enabled;
1215
* • the visualization comes from `appearance.allowedVisualizations`
1316
* (a single entry renders no switcher);
@@ -439,9 +442,21 @@ export function InterfaceListPage({ page, className, onConfigChange, reserveEdit
439442
// Columns: the page's own `columns` win; else the legacy referenced view's;
440443
// else a default from the object so the grid never renders just the
441444
// row-number column.
445+
//
446+
// The view's `hiddenFields` / `fieldOrder` travel WITH the view's columns
447+
// (objectui#10638): the spec composes the three per view — `columns`
448+
// projects, `hiddenFields` subtracts, `fieldOrder` sorts the survivors
449+
// (objectstack#15184 ruling B) — and `ListView`'s `effectiveFields` runs
450+
// that composition, so this page only delivers the two values. They apply
451+
// on the middle branch alone. The page config declares neither key, and
452+
// its own `columns` are "defined directly on the page (no view
453+
// inheritance)" — a view order would otherwise re-sort the very list the
454+
// design-mode column drag saves as `columns`. An empty view `columns`
455+
// "declares no projection, so neither of them applies".
456+
const viewComposes = !hasColumns(cfg) && hasColumns(view);
442457
const columns = hasColumns(cfg)
443458
? (cfg.columns as any)
444-
: hasColumns(view)
459+
: viewComposes
445460
? view.columns
446461
: defaultColumnsFromObject(objectDef, { orgAttribution });
447462

@@ -459,6 +474,8 @@ export function InterfaceListPage({ page, className, onConfigChange, reserveEdit
459474
// The assertion changes no value; the runtime string is what it was.
460475
viewType: (allowed[0] ?? view.type ?? 'grid') as ListViewSchema['viewType'],
461476
columns,
477+
...(viewComposes && view.hiddenFields !== undefined ? { hiddenFields: view.hiddenFields } : {}),
478+
...(viewComposes && view.fieldOrder !== undefined ? { fieldOrder: view.fieldOrder } : {}),
462479
...(filters.length ? { filter: filters } : {}),
463480
...(sort?.length ? { sort } : {}),
464481
grouping: view.grouping,
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* objectui#10638 — an interface page relays its source view's `hiddenFields`
5+
* and `fieldOrder`.
6+
*
7+
* ## The defect this pins
8+
*
9+
* An ADR-0047 interface page does not hand `ListView` the object's list views;
10+
* it builds ONE list schema itself, hand-projecting keys off the view its
11+
* `interfaceConfig.sourceView` resolves to (`filter`, `columns`, `sort`, …).
12+
* `hiddenFields` and `fieldOrder` — the subtraction and the ordering of the
13+
* per-view field composition — were not among them. So a source view that
14+
* authored either showed every column of its `columns`, in `columns` order:
15+
* declared by the protocol, accepted and served, then dropped at this page.
16+
* It is the objectui#7516 relay on the second door (`ObjectView` carries both
17+
* since that card).
18+
*
19+
* ## What the relay carries, and what it does not decide
20+
*
21+
* objectstack#15184 ruling B wrote the composition into the contract, per
22+
* view: `columns` projects, `hiddenFields` subtracts, `fieldOrder` sorts what
23+
* survives (an unlisted survivor sorts last). `ListView`'s `effectiveFields`
24+
* memo runs those steps on whatever it receives, so this page composes
25+
* nothing — it only delivers the view's two values beside the view's
26+
* `columns`.
27+
*
28+
* ## The precedence, and why it is whole-composition
29+
*
30+
* The page's column list resolves `page → source view → object default`, and
31+
* the two keys resolve with it, as one unit from one source:
32+
*
33+
* - the page's own `columns` win, and then NEITHER view key applies. The page
34+
* config declares no spelling of either key (`InterfacePageConfigSchema` is
35+
* strict), its `columns` is "defined directly on the page (no view
36+
* inheritance)", and `sourceView` is "still honored at runtime as a fallback
37+
* when the page has no own `columns`" — the spec's own words. It is also what
38+
* keeps the design-mode column drag honest: that drag persists the order it
39+
* drew as the page's `columns`, which a relayed view `fieldOrder` would
40+
* re-sort straight back.
41+
* - else the view's `columns`, and its `hiddenFields` / `fieldOrder` with them;
42+
* - else the object-derived default columns, with neither key: an empty
43+
* `columns` "declares no projection, so neither of them applies" (the spec,
44+
* on `ListViewSchema.columns`).
45+
*
46+
* ## Why the schema is captured rather than rendered
47+
*
48+
* The claim is about what THIS page hands down, so `ListView` is stubbed and
49+
* its `schema` prop recorded — the posture of
50+
* `ObjectView.fieldOrderRelay-7516.test.tsx`. How the captured keys then shape
51+
* the columns is `plugin-list`'s half, and is not re-pinned here.
52+
*/
53+
54+
import { describe, it, expect, vi, beforeEach } from 'vitest';
55+
import { render, waitFor, cleanup } from '@testing-library/react';
56+
import React from 'react';
57+
58+
vi.mock('react-router-dom', () => ({
59+
useSearchParams: () => [new URLSearchParams(), vi.fn()],
60+
useNavigate: () => vi.fn(),
61+
}));
62+
63+
vi.mock('@object-ui/i18n', async (importOriginal) => {
64+
const actual = await (importOriginal as any)();
65+
return {
66+
...actual,
67+
useObjectTranslation: () => ({ t: (_k: string, o?: any) => o?.defaultValue ?? _k }),
68+
};
69+
});
70+
71+
vi.mock('@object-ui/auth', async (importOriginal) => {
72+
const actual = await (importOriginal as any)();
73+
return { ...actual, useAuth: () => ({}) };
74+
});
75+
76+
/** The list schema this page hands down — captured, not rendered. */
77+
let captured: any = null;
78+
vi.mock('@object-ui/plugin-list', async (importOriginal) => ({
79+
...(await importOriginal<typeof import('@object-ui/plugin-list')>()),
80+
ListView: (props: any) => {
81+
captured = props.schema;
82+
return null;
83+
},
84+
}));
85+
86+
let testObjects: any[];
87+
88+
vi.mock('@object-ui/react', async (importOriginal) => {
89+
const actual = await (importOriginal as any)();
90+
return {
91+
...actual,
92+
useAdapter: () => ({}),
93+
useMetadata: () => ({ objects: testObjects }),
94+
};
95+
});
96+
97+
import { InterfaceListPage } from './InterfaceListPage';
98+
99+
const OBJECT_NAME = 'duly_task';
100+
const VIEW_KEY = 'by_stage';
101+
102+
/** The view's projection — the H1 fixture's three columns, in this order. */
103+
const VIEW_COLUMNS = ['name', 'owner', 'stage'];
104+
const VIEW_HIDDEN = ['owner'];
105+
const VIEW_FIELD_ORDER = ['stage', 'name'];
106+
/** A page-level projection. Distinct from the view's so a crossed wire fails. */
107+
const PAGE_COLUMNS = ['owner', 'name'];
108+
109+
function objectWith(view: Record<string, unknown>) {
110+
return {
111+
name: OBJECT_NAME,
112+
label: 'Task',
113+
fields: {
114+
name: { type: 'text', label: 'Name' },
115+
stage: { type: 'text', label: 'Stage' },
116+
owner: { type: 'text', label: 'Owner' },
117+
},
118+
listViews: {
119+
[`${OBJECT_NAME}.${VIEW_KEY}`]: {
120+
name: `${OBJECT_NAME}.${VIEW_KEY}`,
121+
label: 'By stage',
122+
type: 'grid',
123+
columns: VIEW_COLUMNS,
124+
...view,
125+
},
126+
},
127+
};
128+
}
129+
130+
function pageWith(cfg: Record<string, unknown> = {}) {
131+
return {
132+
name: 'duly_task_board',
133+
label: 'Task board',
134+
interfaceConfig: {
135+
source: OBJECT_NAME,
136+
sourceView: VIEW_KEY,
137+
recordAction: 'none',
138+
...cfg,
139+
},
140+
};
141+
}
142+
143+
/** Render the page and return the whole schema it handed `ListView`. */
144+
async function relayed(view: Record<string, unknown>, cfg?: Record<string, unknown>): Promise<any> {
145+
captured = null;
146+
testObjects = [objectWith(view)];
147+
render(<InterfaceListPage page={pageWith(cfg) as any} />);
148+
// `columns` is written unconditionally by the same object literal as the
149+
// keys under test, so its arrival is the signal the page built its schema —
150+
// waiting on `hiddenFields` itself would hang rather than fail on a
151+
// regression.
152+
await waitFor(() => {
153+
expect(captured?.columns).toBeTruthy();
154+
});
155+
return captured;
156+
}
157+
158+
beforeEach(() => {
159+
cleanup();
160+
captured = null;
161+
testObjects = [];
162+
});
163+
164+
describe("InterfaceListPage relays its source view's hiddenFields and fieldOrder (objectui#10638)", () => {
165+
it("THE FIX: the source view's `hiddenFields` reaches the renderer, verbatim", async () => {
166+
const schema = await relayed({ hiddenFields: VIEW_HIDDEN });
167+
expect(schema.hiddenFields).toEqual(VIEW_HIDDEN);
168+
});
169+
170+
it("THE FIX: the source view's `fieldOrder` reaches the renderer, verbatim", async () => {
171+
// Verbatim, because the order IS the value: the page sorts nothing and
172+
// filters nothing — composing it with `columns` and `hiddenFields` is
173+
// `ListView`'s job.
174+
const schema = await relayed({ fieldOrder: VIEW_FIELD_ORDER });
175+
expect(schema.fieldOrder).toEqual(VIEW_FIELD_ORDER);
176+
});
177+
178+
it("THE FIX: the view's whole composition arrives together — `columns`, `hiddenFields`, `fieldOrder`", async () => {
179+
// The card's first measurement, as a pin: before the relay this schema
180+
// carried the three columns and nothing else, so the page drew
181+
// name / owner / stage in `columns` order.
182+
const schema = await relayed({ hiddenFields: VIEW_HIDDEN, fieldOrder: VIEW_FIELD_ORDER });
183+
expect(schema.columns).toEqual(VIEW_COLUMNS);
184+
expect(schema.hiddenFields).toEqual(VIEW_HIDDEN);
185+
expect(schema.fieldOrder).toEqual(VIEW_FIELD_ORDER);
186+
});
187+
188+
it("PRECEDENCE: the page's own `columns` replace the view's composition whole — neither view key applies", async () => {
189+
// Green on the base by construction (the base relays neither key); what
190+
// this case fails is a relay that ignores the precedence and lets the
191+
// view's `fieldOrder` re-sort, or its `hiddenFields` thin, a column list
192+
// the page defined for itself.
193+
const schema = await relayed(
194+
{ hiddenFields: VIEW_HIDDEN, fieldOrder: VIEW_FIELD_ORDER },
195+
{ columns: PAGE_COLUMNS },
196+
);
197+
expect(schema.columns).toEqual(PAGE_COLUMNS);
198+
expect('hiddenFields' in schema).toBe(false);
199+
expect('fieldOrder' in schema).toBe(false);
200+
});
201+
202+
it('CONTROL: a view with an empty `columns` declares no projection — defaults are derived and neither key applies', async () => {
203+
const schema = await relayed({
204+
columns: [],
205+
hiddenFields: VIEW_HIDDEN,
206+
fieldOrder: VIEW_FIELD_ORDER,
207+
});
208+
// The object-derived default, not the view's (empty) list.
209+
expect(schema.columns).toEqual(['name', 'stage', 'owner']);
210+
expect('hiddenFields' in schema).toBe(false);
211+
expect('fieldOrder' in schema).toBe(false);
212+
});
213+
214+
it('CONTROL: a view that authors neither key leaves the schema without them', async () => {
215+
// Absent, not `undefined`-valued: the schema's key set is what it was.
216+
const schema = await relayed({});
217+
expect(schema.columns).toEqual(VIEW_COLUMNS);
218+
expect('hiddenFields' in schema).toBe(false);
219+
expect('fieldOrder' in schema).toBe(false);
220+
});
221+
});

0 commit comments

Comments
 (0)