@fogpipe/forma-react 0.6.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.
@@ -0,0 +1,803 @@
1
+ /// <reference types="@testing-library/jest-dom" />
2
+ /**
3
+ * Tests for FormRenderer component
4
+ */
5
+
6
+ import { describe, it, expect, vi } from "vitest";
7
+ import { render, screen, waitFor, act } from "@testing-library/react";
8
+ import { userEvent } from "@testing-library/user-event";
9
+ import { useRef } from "react";
10
+ import { FormRenderer } from "../FormRenderer.js";
11
+ import { useFormaContext } from "../context.js";
12
+ import { createTestSpec, createTestComponentMap } from "./test-utils.js";
13
+ import type { FormRendererHandle } from "../FormRenderer.js";
14
+ import type { LayoutProps, PageWrapperProps, ComponentMap } from "../types.js";
15
+ import type { UseFormaReturn } from "../useForma.js";
16
+
17
+ describe("FormRenderer", () => {
18
+ // ============================================================================
19
+ // Basic Rendering
20
+ // ============================================================================
21
+
22
+ describe("basic rendering", () => {
23
+ it("should render fields from spec", () => {
24
+ const spec = createTestSpec({
25
+ fields: {
26
+ name: { type: "text", label: "Name" },
27
+ email: { type: "email", label: "Email" },
28
+ },
29
+ });
30
+
31
+ render(
32
+ <FormRenderer
33
+ spec={spec}
34
+ components={createTestComponentMap()}
35
+ />
36
+ );
37
+
38
+ expect(screen.getByTestId("field-name")).toBeInTheDocument();
39
+ expect(screen.getByTestId("field-email")).toBeInTheDocument();
40
+ });
41
+
42
+ it("should render fields in fieldOrder", () => {
43
+ const spec = createTestSpec({
44
+ fields: {
45
+ a: { type: "text", label: "A" },
46
+ b: { type: "text", label: "B" },
47
+ c: { type: "text", label: "C" },
48
+ },
49
+ fieldOrder: ["c", "a", "b"],
50
+ });
51
+
52
+ const { container } = render(
53
+ <FormRenderer
54
+ spec={spec}
55
+ components={createTestComponentMap()}
56
+ />
57
+ );
58
+
59
+ const fields = container.querySelectorAll("[data-testid^='field-']");
60
+ expect(fields[0]).toHaveAttribute("data-testid", "field-c");
61
+ expect(fields[1]).toHaveAttribute("data-testid", "field-a");
62
+ expect(fields[2]).toHaveAttribute("data-testid", "field-b");
63
+ });
64
+
65
+ it("should render with initial data", () => {
66
+ const spec = createTestSpec({
67
+ fields: {
68
+ name: { type: "text", label: "Name" },
69
+ },
70
+ });
71
+
72
+ render(
73
+ <FormRenderer
74
+ spec={spec}
75
+ initialData={{ name: "John Doe" }}
76
+ components={createTestComponentMap()}
77
+ />
78
+ );
79
+
80
+ const input = screen.getByRole("textbox");
81
+ expect(input).toHaveValue("John Doe");
82
+ });
83
+
84
+ it("should use custom layout component", () => {
85
+ const spec = createTestSpec({
86
+ fields: { name: { type: "text" } },
87
+ });
88
+
89
+ const CustomLayout = ({ children }: LayoutProps) => (
90
+ <div data-testid="custom-layout">{children}</div>
91
+ );
92
+
93
+ render(
94
+ <FormRenderer
95
+ spec={spec}
96
+ components={createTestComponentMap()}
97
+ layout={CustomLayout}
98
+ />
99
+ );
100
+
101
+ expect(screen.getByTestId("custom-layout")).toBeInTheDocument();
102
+ });
103
+ });
104
+
105
+ // ============================================================================
106
+ // Field Type Rendering
107
+ // ============================================================================
108
+
109
+ describe("field type rendering", () => {
110
+ it("should render text field", () => {
111
+ const spec = createTestSpec({
112
+ fields: { name: { type: "text" } },
113
+ });
114
+
115
+ render(
116
+ <FormRenderer
117
+ spec={spec}
118
+ components={createTestComponentMap()}
119
+ />
120
+ );
121
+
122
+ expect(screen.getByRole("textbox")).toBeInTheDocument();
123
+ });
124
+
125
+ it("should render number field", () => {
126
+ const spec = createTestSpec({
127
+ fields: { age: { type: "number" } },
128
+ });
129
+
130
+ render(
131
+ <FormRenderer
132
+ spec={spec}
133
+ components={createTestComponentMap()}
134
+ />
135
+ );
136
+
137
+ expect(screen.getByRole("spinbutton")).toBeInTheDocument();
138
+ });
139
+
140
+ it("should render boolean field", () => {
141
+ const spec = createTestSpec({
142
+ fields: { agree: { type: "boolean" } },
143
+ });
144
+
145
+ render(
146
+ <FormRenderer
147
+ spec={spec}
148
+ components={createTestComponentMap()}
149
+ />
150
+ );
151
+
152
+ expect(screen.getByRole("checkbox")).toBeInTheDocument();
153
+ });
154
+
155
+ it("should render select field with options", () => {
156
+ const spec = createTestSpec({
157
+ fields: {
158
+ country: {
159
+ type: "select",
160
+ options: [
161
+ { value: "us", label: "United States" },
162
+ { value: "ca", label: "Canada" },
163
+ ],
164
+ },
165
+ },
166
+ });
167
+
168
+ render(
169
+ <FormRenderer
170
+ spec={spec}
171
+ components={createTestComponentMap()}
172
+ />
173
+ );
174
+
175
+ expect(screen.getByRole("combobox")).toBeInTheDocument();
176
+ expect(screen.getByText("United States")).toBeInTheDocument();
177
+ expect(screen.getByText("Canada")).toBeInTheDocument();
178
+ });
179
+
180
+ it("should render array field", () => {
181
+ const spec = createTestSpec({
182
+ fields: {
183
+ items: {
184
+ type: "array",
185
+ label: "Items",
186
+ itemFields: { name: { type: "text" } },
187
+ },
188
+ },
189
+ });
190
+
191
+ render(
192
+ <FormRenderer
193
+ spec={spec}
194
+ initialData={{ items: [{ name: "Item 1" }] }}
195
+ components={createTestComponentMap()}
196
+ />
197
+ );
198
+
199
+ expect(screen.getByTestId("field-items")).toBeInTheDocument();
200
+ expect(screen.getByTestId("add-items")).toBeInTheDocument();
201
+ });
202
+ });
203
+
204
+ // ============================================================================
205
+ // User Interactions
206
+ // ============================================================================
207
+
208
+ describe("user interactions", () => {
209
+ it("should update value on text input", async () => {
210
+ const user = userEvent.setup();
211
+ const onChange = vi.fn();
212
+ const spec = createTestSpec({
213
+ fields: { name: { type: "text" } },
214
+ });
215
+
216
+ render(
217
+ <FormRenderer
218
+ spec={spec}
219
+ components={createTestComponentMap()}
220
+ onChange={onChange}
221
+ />
222
+ );
223
+
224
+ const input = screen.getByRole("textbox");
225
+ await user.type(input, "Hello");
226
+
227
+ expect(input).toHaveValue("Hello");
228
+ expect(onChange).toHaveBeenCalled();
229
+ });
230
+
231
+ it("should update value on checkbox toggle", async () => {
232
+ const user = userEvent.setup();
233
+ const spec = createTestSpec({
234
+ fields: { agree: { type: "boolean" } },
235
+ });
236
+
237
+ render(
238
+ <FormRenderer
239
+ spec={spec}
240
+ initialData={{ agree: false }}
241
+ components={createTestComponentMap()}
242
+ />
243
+ );
244
+
245
+ const checkbox = screen.getByRole("checkbox");
246
+ expect(checkbox).not.toBeChecked();
247
+
248
+ await user.click(checkbox);
249
+ expect(checkbox).toBeChecked();
250
+ });
251
+
252
+ it("should update value on select change", async () => {
253
+ const user = userEvent.setup();
254
+ const spec = createTestSpec({
255
+ fields: {
256
+ country: {
257
+ type: "select",
258
+ options: [
259
+ { value: "us", label: "United States" },
260
+ { value: "ca", label: "Canada" },
261
+ ],
262
+ },
263
+ },
264
+ });
265
+
266
+ render(
267
+ <FormRenderer
268
+ spec={spec}
269
+ components={createTestComponentMap()}
270
+ />
271
+ );
272
+
273
+ const select = screen.getByRole("combobox");
274
+ await user.selectOptions(select, "ca");
275
+
276
+ expect(select).toHaveValue("ca");
277
+ });
278
+ });
279
+
280
+ // ============================================================================
281
+ // Form Submission
282
+ // ============================================================================
283
+
284
+ describe("form submission", () => {
285
+ it("should call onSubmit with form data", async () => {
286
+ const user = userEvent.setup();
287
+ const onSubmit = vi.fn();
288
+ const spec = createTestSpec({
289
+ fields: { name: { type: "text" } },
290
+ });
291
+
292
+ render(
293
+ <FormRenderer
294
+ spec={spec}
295
+ initialData={{ name: "John" }}
296
+ onSubmit={onSubmit}
297
+ components={createTestComponentMap()}
298
+ />
299
+ );
300
+
301
+ const submitButton = screen.getByRole("button", { name: /submit/i });
302
+ await user.click(submitButton);
303
+
304
+ expect(onSubmit).toHaveBeenCalledWith({ name: "John" });
305
+ });
306
+
307
+ it("should not submit when form is invalid", async () => {
308
+ const user = userEvent.setup();
309
+ const onSubmit = vi.fn();
310
+ const spec = createTestSpec({
311
+ fields: { name: { type: "text", required: true } },
312
+ });
313
+
314
+ render(
315
+ <FormRenderer
316
+ spec={spec}
317
+ onSubmit={onSubmit}
318
+ components={createTestComponentMap()}
319
+ />
320
+ );
321
+
322
+ const submitButton = screen.getByRole("button", { name: /submit/i });
323
+ await user.click(submitButton);
324
+
325
+ expect(onSubmit).not.toHaveBeenCalled();
326
+ });
327
+
328
+ it("should show validation errors after submit attempt", async () => {
329
+ const user = userEvent.setup();
330
+ const spec = createTestSpec({
331
+ fields: { name: { type: "text", required: true } },
332
+ });
333
+
334
+ render(
335
+ <FormRenderer
336
+ spec={spec}
337
+ components={createTestComponentMap()}
338
+ />
339
+ );
340
+
341
+ const submitButton = screen.getByRole("button", { name: /submit/i });
342
+ await user.click(submitButton);
343
+
344
+ // Error should now be visible
345
+ await waitFor(() => {
346
+ expect(screen.getByTestId("error-name")).toBeInTheDocument();
347
+ });
348
+ });
349
+
350
+ it("should disable submit button while submitting", async () => {
351
+ const user = userEvent.setup();
352
+ const onSubmit = vi.fn(
353
+ (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 100))
354
+ );
355
+ const spec = createTestSpec({
356
+ fields: { name: { type: "text" } },
357
+ });
358
+
359
+ render(
360
+ <FormRenderer
361
+ spec={spec}
362
+ initialData={{ name: "John" }}
363
+ onSubmit={onSubmit}
364
+ components={createTestComponentMap()}
365
+ />
366
+ );
367
+
368
+ const submitButton = screen.getByRole("button", { name: /submit/i });
369
+ await user.click(submitButton);
370
+
371
+ expect(submitButton).toBeDisabled();
372
+ expect(submitButton).toHaveTextContent("Submitting...");
373
+
374
+ await waitFor(() => {
375
+ expect(submitButton).not.toBeDisabled();
376
+ });
377
+ });
378
+ });
379
+
380
+ // ============================================================================
381
+ // Imperative Handle (ref)
382
+ // ============================================================================
383
+
384
+ describe("imperative handle (ref)", () => {
385
+ it("should expose submitForm via ref", async () => {
386
+ const onSubmit = vi.fn();
387
+ const spec = createTestSpec({
388
+ fields: { name: { type: "text" } },
389
+ });
390
+
391
+ let formRef: React.RefObject<FormRendererHandle | null>;
392
+
393
+ function TestComponent() {
394
+ formRef = useRef<FormRendererHandle>(null);
395
+ return (
396
+ <FormRenderer
397
+ ref={formRef}
398
+ spec={spec}
399
+ initialData={{ name: "Test" }}
400
+ onSubmit={onSubmit}
401
+ components={createTestComponentMap()}
402
+ />
403
+ );
404
+ }
405
+
406
+ render(<TestComponent />);
407
+
408
+ await act(async () => {
409
+ await formRef!.current?.submitForm();
410
+ });
411
+
412
+ expect(onSubmit).toHaveBeenCalledWith({ name: "Test" });
413
+ });
414
+
415
+ it("should expose resetForm via ref", async () => {
416
+ const spec = createTestSpec({
417
+ fields: { name: { type: "text" } },
418
+ });
419
+
420
+ let formRef: React.RefObject<FormRendererHandle | null>;
421
+
422
+ function TestComponent() {
423
+ formRef = useRef<FormRendererHandle>(null);
424
+ return (
425
+ <FormRenderer
426
+ ref={formRef}
427
+ spec={spec}
428
+ initialData={{ name: "Original" }}
429
+ components={createTestComponentMap()}
430
+ />
431
+ );
432
+ }
433
+
434
+ const { rerender } = render(<TestComponent />);
435
+ const user = userEvent.setup();
436
+
437
+ const input = screen.getByRole("textbox");
438
+ await user.clear(input);
439
+ await user.type(input, "Changed");
440
+ expect(input).toHaveValue("Changed");
441
+
442
+ act(() => {
443
+ formRef!.current?.resetForm();
444
+ });
445
+ rerender(<TestComponent />);
446
+
447
+ await waitFor(() => {
448
+ expect(screen.getByRole("textbox")).toHaveValue("Original");
449
+ });
450
+ });
451
+
452
+ it("should expose validateForm via ref", () => {
453
+ const spec = createTestSpec({
454
+ fields: { name: { type: "text", required: true } },
455
+ });
456
+
457
+ let formRef: React.RefObject<FormRendererHandle | null>;
458
+
459
+ function TestComponent() {
460
+ formRef = useRef<FormRendererHandle>(null);
461
+ return (
462
+ <FormRenderer
463
+ ref={formRef}
464
+ spec={spec}
465
+ components={createTestComponentMap()}
466
+ />
467
+ );
468
+ }
469
+
470
+ render(<TestComponent />);
471
+
472
+ const result = formRef!.current?.validateForm();
473
+
474
+ expect(result?.valid).toBe(false);
475
+ expect(result?.errors.length).toBeGreaterThan(0);
476
+ });
477
+
478
+ it("should expose getValues via ref", () => {
479
+ const spec = createTestSpec({
480
+ fields: { name: { type: "text" } },
481
+ });
482
+
483
+ let formRef: React.RefObject<FormRendererHandle | null>;
484
+
485
+ function TestComponent() {
486
+ formRef = useRef<FormRendererHandle>(null);
487
+ return (
488
+ <FormRenderer
489
+ ref={formRef}
490
+ spec={spec}
491
+ initialData={{ name: "Test Value" }}
492
+ components={createTestComponentMap()}
493
+ />
494
+ );
495
+ }
496
+
497
+ render(<TestComponent />);
498
+
499
+ const values = formRef!.current?.getValues();
500
+
501
+ expect(values).toEqual({ name: "Test Value" });
502
+ });
503
+
504
+ it("should expose setValues via ref", async () => {
505
+ const spec = createTestSpec({
506
+ fields: { name: { type: "text" } },
507
+ });
508
+
509
+ let formRef: React.RefObject<FormRendererHandle | null>;
510
+
511
+ function TestComponent() {
512
+ formRef = useRef<FormRendererHandle>(null);
513
+ return (
514
+ <FormRenderer
515
+ ref={formRef}
516
+ spec={spec}
517
+ components={createTestComponentMap()}
518
+ />
519
+ );
520
+ }
521
+
522
+ const { rerender } = render(<TestComponent />);
523
+
524
+ act(() => {
525
+ formRef!.current?.setValues({ name: "New Value" });
526
+ });
527
+ rerender(<TestComponent />);
528
+
529
+ await waitFor(() => {
530
+ expect(screen.getByRole("textbox")).toHaveValue("New Value");
531
+ });
532
+ });
533
+
534
+ it("should expose isValid and isDirty via ref", async () => {
535
+ const spec = createTestSpec({
536
+ fields: { name: { type: "text", required: true } },
537
+ });
538
+
539
+ let formRef: React.RefObject<FormRendererHandle | null>;
540
+
541
+ function TestComponent() {
542
+ formRef = useRef<FormRendererHandle>(null);
543
+ return (
544
+ <FormRenderer
545
+ ref={formRef}
546
+ spec={spec}
547
+ components={createTestComponentMap()}
548
+ />
549
+ );
550
+ }
551
+
552
+ const { rerender } = render(<TestComponent />);
553
+
554
+ expect(formRef!.current?.isValid).toBe(false);
555
+ expect(formRef!.current?.isDirty).toBe(false);
556
+
557
+ act(() => {
558
+ formRef!.current?.setValues({ name: "Value" });
559
+ });
560
+ rerender(<TestComponent />);
561
+
562
+ await waitFor(() => {
563
+ expect(formRef!.current?.isValid).toBe(true);
564
+ expect(formRef!.current?.isDirty).toBe(true);
565
+ });
566
+ });
567
+ });
568
+
569
+ // ============================================================================
570
+ // Context
571
+ // ============================================================================
572
+
573
+ describe("context", () => {
574
+ it("should provide form state via FormaContext", () => {
575
+ const spec = createTestSpec({
576
+ fields: { name: { type: "text" } },
577
+ });
578
+
579
+ let contextValue: UseFormaReturn | null = null;
580
+
581
+ function ContextConsumer() {
582
+ contextValue = useFormaContext();
583
+ return <div data-testid="consumer">Consumer</div>;
584
+ }
585
+
586
+ // Custom component that uses context
587
+ const components: ComponentMap = {
588
+ ...createTestComponentMap(),
589
+ text: () => <ContextConsumer />,
590
+ };
591
+
592
+ render(
593
+ <FormRenderer
594
+ spec={spec}
595
+ initialData={{ name: "Test" }}
596
+ components={components}
597
+ />
598
+ );
599
+
600
+ expect(screen.getByTestId("consumer")).toBeInTheDocument();
601
+ expect(contextValue).not.toBeNull();
602
+ expect(contextValue!.data).toEqual({ name: "Test" });
603
+ });
604
+
605
+ it("should throw error when useFormaContext used outside provider", () => {
606
+ function BadComponent() {
607
+ useFormaContext();
608
+ return null;
609
+ }
610
+
611
+ expect(() => render(<BadComponent />)).toThrow(
612
+ /useFormaContext must be used within/
613
+ );
614
+ });
615
+ });
616
+
617
+ // ============================================================================
618
+ // Wizard / Multi-page Forms
619
+ // ============================================================================
620
+
621
+ describe("wizard / multi-page forms", () => {
622
+ it("should render fields for current page only", () => {
623
+ const spec = createTestSpec({
624
+ fields: {
625
+ name: { type: "text", label: "Name" },
626
+ email: { type: "email", label: "Email" },
627
+ },
628
+ pages: [
629
+ { id: "page1", title: "Step 1", fields: ["name"] },
630
+ { id: "page2", title: "Step 2", fields: ["email"] },
631
+ ],
632
+ });
633
+
634
+ render(
635
+ <FormRenderer
636
+ spec={spec}
637
+ components={createTestComponentMap()}
638
+ />
639
+ );
640
+
641
+ // First page should be visible
642
+ expect(screen.getByTestId("field-name")).toBeInTheDocument();
643
+ // Second page should not be visible
644
+ expect(screen.queryByTestId("field-email")).not.toBeInTheDocument();
645
+ });
646
+
647
+ it("should use custom page wrapper", () => {
648
+ const spec = createTestSpec({
649
+ fields: { name: { type: "text" } },
650
+ pages: [{ id: "page1", title: "Step 1", fields: ["name"] }],
651
+ });
652
+
653
+ // Note: forma-oss passes title/description directly, not a page object
654
+ const CustomPageWrapper = ({ title, children }: PageWrapperProps) => (
655
+ <div data-testid="custom-page" data-page-title={title}>
656
+ {children}
657
+ </div>
658
+ );
659
+
660
+ render(
661
+ <FormRenderer
662
+ spec={spec}
663
+ components={createTestComponentMap()}
664
+ pageWrapper={CustomPageWrapper}
665
+ />
666
+ );
667
+
668
+ const pageWrapper = screen.getByTestId("custom-page");
669
+ expect(pageWrapper).toBeInTheDocument();
670
+ expect(pageWrapper).toHaveAttribute("data-page-title", "Step 1");
671
+ });
672
+ });
673
+
674
+ // ============================================================================
675
+ // Visibility
676
+ // ============================================================================
677
+
678
+ describe("visibility", () => {
679
+ it("should hide fields when visibility is false", () => {
680
+ const spec = createTestSpec({
681
+ fields: {
682
+ showDetails: { type: "boolean", label: "Show Details" },
683
+ details: {
684
+ type: "text",
685
+ label: "Details",
686
+ visibleWhen: "showDetails = true",
687
+ },
688
+ },
689
+ });
690
+
691
+ render(
692
+ <FormRenderer
693
+ spec={spec}
694
+ initialData={{ showDetails: false }}
695
+ components={createTestComponentMap()}
696
+ />
697
+ );
698
+
699
+ // Details field should not be rendered when hidden
700
+ expect(screen.queryByTestId("field-details")).not.toBeInTheDocument();
701
+ });
702
+
703
+ it("should show fields when visibility becomes true", async () => {
704
+ const user = userEvent.setup();
705
+ const spec = createTestSpec({
706
+ fields: {
707
+ showDetails: { type: "boolean", label: "Show Details" },
708
+ details: {
709
+ type: "text",
710
+ label: "Details",
711
+ visibleWhen: "showDetails = true",
712
+ },
713
+ },
714
+ });
715
+
716
+ render(
717
+ <FormRenderer
718
+ spec={spec}
719
+ initialData={{ showDetails: false }}
720
+ components={createTestComponentMap()}
721
+ />
722
+ );
723
+
724
+ // Initially hidden
725
+ expect(screen.queryByTestId("field-details")).not.toBeInTheDocument();
726
+
727
+ // Click checkbox to show
728
+ const checkbox = screen.getByRole("checkbox");
729
+ await user.click(checkbox);
730
+
731
+ // Now visible
732
+ await waitFor(() => {
733
+ expect(screen.getByTestId("field-details")).toBeInTheDocument();
734
+ });
735
+ });
736
+ });
737
+
738
+ // ============================================================================
739
+ // Array Field Interactions
740
+ // ============================================================================
741
+
742
+ describe("array field interactions", () => {
743
+ it("should add item when add button clicked", async () => {
744
+ const user = userEvent.setup();
745
+ const spec = createTestSpec({
746
+ fields: {
747
+ items: {
748
+ type: "array",
749
+ label: "Items",
750
+ itemFields: { name: { type: "text" } },
751
+ },
752
+ },
753
+ });
754
+
755
+ render(
756
+ <FormRenderer
757
+ spec={spec}
758
+ initialData={{ items: [] }}
759
+ components={createTestComponentMap()}
760
+ />
761
+ );
762
+
763
+ const addButton = screen.getByTestId("add-items");
764
+ await user.click(addButton);
765
+
766
+ await waitFor(() => {
767
+ expect(screen.getByTestId("array-item-items-0")).toBeInTheDocument();
768
+ });
769
+ });
770
+
771
+ it("should remove item when remove button clicked", async () => {
772
+ const user = userEvent.setup();
773
+ const spec = createTestSpec({
774
+ fields: {
775
+ items: {
776
+ type: "array",
777
+ label: "Items",
778
+ itemFields: { name: { type: "text" } },
779
+ },
780
+ },
781
+ });
782
+
783
+ render(
784
+ <FormRenderer
785
+ spec={spec}
786
+ initialData={{ items: [{ name: "Item 1" }, { name: "Item 2" }] }}
787
+ components={createTestComponentMap()}
788
+ />
789
+ );
790
+
791
+ expect(screen.getByTestId("array-item-items-0")).toBeInTheDocument();
792
+ expect(screen.getByTestId("array-item-items-1")).toBeInTheDocument();
793
+
794
+ const removeButton = screen.getByTestId("remove-items-0");
795
+ await user.click(removeButton);
796
+
797
+ await waitFor(() => {
798
+ // After removing first item, we should have only 1 item (at index 0)
799
+ expect(screen.queryByTestId("array-item-items-1")).not.toBeInTheDocument();
800
+ });
801
+ });
802
+ });
803
+ });