@woltz/rich-domain 1.0.0 → 1.2.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/CHANGELOG.md +37 -86
- package/LICENSE +20 -20
- package/dist/base-entity.d.ts +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +15 -19
- package/dist/base-entity.js.map +1 -1
- package/dist/constants.js +1 -4
- package/dist/constants.js.map +1 -1
- package/dist/criteria.d.ts +24 -14
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +154 -67
- package/dist/criteria.js.map +1 -1
- package/dist/deep-proxy.d.ts.map +1 -1
- package/dist/deep-proxy.js +19 -9
- package/dist/deep-proxy.js.map +1 -1
- package/dist/domain-event-bus.d.ts +5 -6
- package/dist/domain-event-bus.d.ts.map +1 -1
- package/dist/domain-event-bus.js +4 -17
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.d.ts +1 -31
- package/dist/domain-event.d.ts.map +1 -1
- package/dist/domain-event.js +4 -7
- package/dist/domain-event.js.map +1 -1
- package/dist/entity.d.ts +2 -2
- package/dist/entity.js +3 -8
- package/dist/entity.js.map +1 -1
- package/dist/exceptions.d.ts +251 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +321 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +14 -7
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -40
- package/dist/index.js.map +1 -1
- package/dist/mapper.js +1 -5
- package/dist/mapper.js.map +1 -1
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +17 -9
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/base-repository.js +4 -11
- package/dist/repository/base-repository.js.map +1 -1
- package/dist/repository/index.d.ts +1 -2
- package/dist/repository/index.d.ts.map +1 -1
- package/dist/repository/index.js +3 -26
- package/dist/repository/index.js.map +1 -1
- package/dist/repository/unit-of-work.d.ts +0 -11
- package/dist/repository/unit-of-work.d.ts.map +1 -1
- package/dist/repository/unit-of-work.js +2 -43
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/criteria.d.ts +31 -7
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/criteria.js +1 -4
- package/dist/types/criteria.js.map +1 -1
- package/dist/types/domain-event.d.ts +32 -0
- package/dist/types/domain-event.d.ts.map +1 -0
- package/dist/types/domain-event.js +2 -0
- package/dist/types/domain-event.js.map +1 -0
- package/dist/types/domain.d.ts +2 -2
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/domain.js +1 -2
- package/dist/types/history-tracker.d.ts +1 -1
- package/dist/types/history-tracker.d.ts.map +1 -1
- package/dist/types/history-tracker.js +1 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -22
- package/dist/types/index.js.map +1 -1
- package/dist/types/standard-schema.js +1 -2
- package/dist/types/unit-of-work.js +1 -2
- package/dist/types/utils.js +1 -2
- package/dist/utils/criteria-operator-validation.d.ts +5 -0
- package/dist/utils/criteria-operator-validation.d.ts.map +1 -0
- package/dist/utils/criteria-operator-validation.js +143 -0
- package/dist/utils/criteria-operator-validation.js.map +1 -0
- package/dist/utils/helpers.d.ts +2 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +10 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/validation-error.js +3 -9
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +7 -14
- package/dist/value-object.js.map +1 -1
- package/eslint.config.js +9 -3
- package/jest.config.js +1 -1
- package/package.json +14 -20
- package/src/base-entity.ts +3 -3
- package/src/criteria.ts +268 -87
- package/src/deep-proxy.ts +50 -38
- package/src/domain-event-bus.ts +152 -166
- package/src/domain-event.ts +53 -90
- package/src/entity.ts +16 -16
- package/src/exceptions.ts +435 -0
- package/src/id.ts +107 -94
- package/src/index.ts +32 -8
- package/src/paginated-result.ts +15 -3
- package/src/repository/index.ts +1 -6
- package/src/repository/unit-of-work.ts +1 -44
- package/src/types/criteria.ts +95 -17
- package/src/types/domain-event.ts +38 -0
- package/src/types/domain.ts +2 -3
- package/src/types/history-tracker.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/utils/criteria-operator-validation.ts +171 -0
- package/src/utils/helpers.ts +6 -0
- package/src/validation-error.ts +97 -97
- package/src/value-object.ts +3 -6
- package/tests/criteria.test.ts +324 -1
- package/tests/domain-events.test.ts +431 -445
- package/tests/entity-validation.test.ts +1 -1
- package/tests/entity.test.ts +33 -33
- package/tests/repository.test.ts +4 -2
- package/tests/utils.ts +254 -151
- package/tests/value-object-validation.test.ts +0 -9
- package/tsconfig.json +2 -24
- package/.github/workflows/ci.yml +0 -40
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.vscode/settings.json +0 -3
- package/commitlint.config.js +0 -23
- package/dist/repository/in-memory-repository.d.ts +0 -50
- package/dist/repository/in-memory-repository.d.ts.map +0 -1
- package/dist/repository/in-memory-repository.js +0 -97
- package/dist/repository/in-memory-repository.js.map +0 -1
- package/src/repository/in-memory-repository.ts +0 -116
package/tests/criteria.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Pagination } from "../src";
|
|
2
2
|
import { Criteria } from "../src/criteria";
|
|
3
3
|
import { PaginatedResult } from "../src/paginated-result";
|
|
4
|
+
import { CriteriaAdapter } from "../src/types";
|
|
4
5
|
import { Post } from "./utils";
|
|
5
6
|
|
|
6
7
|
interface TestUser {
|
|
@@ -12,6 +13,17 @@ interface TestUser {
|
|
|
12
13
|
createdAt: Date;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
interface UserWithPostsDto {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
posts: { title: string; content: string }[];
|
|
20
|
+
leads: {
|
|
21
|
+
contact: {
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
const testUsers: TestUser[] = [
|
|
16
28
|
{
|
|
17
29
|
id: "1",
|
|
@@ -56,6 +68,20 @@ const testUsers: TestUser[] = [
|
|
|
56
68
|
];
|
|
57
69
|
|
|
58
70
|
describe("Criteria", () => {
|
|
71
|
+
describe("Search", () => {
|
|
72
|
+
it("should search by all items ignoring pagination and limit", () => {
|
|
73
|
+
const criteria = Criteria.create<TestUser>()
|
|
74
|
+
.search(["name"], "Eve")
|
|
75
|
+
.paginate(3, 1);
|
|
76
|
+
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
77
|
+
expect(result.data).toHaveLength(1);
|
|
78
|
+
expect(result.data[0].name).toBe("Eve");
|
|
79
|
+
expect(result.meta.total).toBe(1);
|
|
80
|
+
expect(result.meta.totalPages).toBe(1);
|
|
81
|
+
expect(result.meta.page).toBe(1);
|
|
82
|
+
expect(result.meta.limit).toBe(1);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
59
85
|
describe("Fluent API", () => {
|
|
60
86
|
it("should create empty criteria", () => {
|
|
61
87
|
const criteria = Criteria.create<TestUser>();
|
|
@@ -113,6 +139,14 @@ describe("Criteria", () => {
|
|
|
113
139
|
expect(result.data.every((u) => u.status === "inactive")).toBe(true);
|
|
114
140
|
});
|
|
115
141
|
|
|
142
|
+
it("should filter by search", () => {
|
|
143
|
+
const criteria = Criteria.create<TestUser>().search(["name"], "Bob");
|
|
144
|
+
|
|
145
|
+
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
146
|
+
expect(result.data).toHaveLength(1);
|
|
147
|
+
expect(result.data[0].name).toBe("Bob");
|
|
148
|
+
});
|
|
149
|
+
|
|
116
150
|
it("should filter by greaterThan", () => {
|
|
117
151
|
const criteria = Criteria.create<TestUser>().where(
|
|
118
152
|
"age",
|
|
@@ -289,7 +323,7 @@ describe("Criteria", () => {
|
|
|
289
323
|
|
|
290
324
|
expect(result.data).toHaveLength(2);
|
|
291
325
|
expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
|
|
292
|
-
expect(result.meta.total).toBe(3);
|
|
326
|
+
expect(result.meta.total).toBe(3);
|
|
293
327
|
expect(result.meta.totalPages).toBe(2);
|
|
294
328
|
});
|
|
295
329
|
});
|
|
@@ -429,4 +463,293 @@ describe("Criteria", () => {
|
|
|
429
463
|
expect(criteria.getPagination()?.limit).toBe(2);
|
|
430
464
|
});
|
|
431
465
|
});
|
|
466
|
+
|
|
467
|
+
describe("Quantifiers", () => {
|
|
468
|
+
describe("Fluent API methods", () => {
|
|
469
|
+
it("should create filter with whereSome", () => {
|
|
470
|
+
const criteria = Criteria.create<UserWithPostsDto>().whereSome(
|
|
471
|
+
"posts.title",
|
|
472
|
+
"contains",
|
|
473
|
+
"test"
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
const filters = criteria.getFilters();
|
|
477
|
+
|
|
478
|
+
expect(filters).toHaveLength(1);
|
|
479
|
+
expect(filters[0].field).toBe("posts.title");
|
|
480
|
+
expect(filters[0].operator).toBe("contains");
|
|
481
|
+
expect(filters[0].value).toBe("test");
|
|
482
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("should create filter with whereEvery", () => {
|
|
486
|
+
const criteria = Criteria.create<UserWithPostsDto>().whereEvery(
|
|
487
|
+
"posts.title",
|
|
488
|
+
"contains",
|
|
489
|
+
"test"
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
const filters = criteria.getFilters();
|
|
493
|
+
expect(filters).toHaveLength(1);
|
|
494
|
+
expect(filters[0].options?.quantifier).toBe("every");
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("should create filter with whereNone", () => {
|
|
498
|
+
const criteria = Criteria.create<UserWithPostsDto>().whereNone(
|
|
499
|
+
"posts.title",
|
|
500
|
+
"contains",
|
|
501
|
+
"test"
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
const filters = criteria.getFilters();
|
|
505
|
+
expect(filters).toHaveLength(1);
|
|
506
|
+
expect(filters[0].options?.quantifier).toBe("none");
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it("should create filter without quantifier using where", () => {
|
|
510
|
+
const criteria = Criteria.create<UserWithPostsDto>().where(
|
|
511
|
+
"posts.title",
|
|
512
|
+
"contains",
|
|
513
|
+
"test"
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
const filters = criteria.getFilters();
|
|
517
|
+
expect(filters).toHaveLength(1);
|
|
518
|
+
expect(filters[0].options).toBeUndefined();
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
describe("fromQueryParams with quantifier", () => {
|
|
523
|
+
it("should parse quantifier from query params with @some", () => {
|
|
524
|
+
const queryParams = {
|
|
525
|
+
"posts.title:contains@some": "test",
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const criteria =
|
|
529
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
530
|
+
const filters = criteria.getFilters();
|
|
531
|
+
|
|
532
|
+
expect(filters).toHaveLength(1);
|
|
533
|
+
expect(filters[0].field).toBe("posts.title");
|
|
534
|
+
expect(filters[0].operator).toBe("contains");
|
|
535
|
+
expect(filters[0].value).toBe("test");
|
|
536
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it("should parse quantifier from query params with @every", () => {
|
|
540
|
+
const queryParams = {
|
|
541
|
+
"posts.title:equals@every": "test",
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const criteria =
|
|
545
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
546
|
+
const filters = criteria.getFilters();
|
|
547
|
+
|
|
548
|
+
expect(filters).toHaveLength(1);
|
|
549
|
+
expect(filters[0].options?.quantifier).toBe("every");
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it("should parse quantifier from query params with @none", () => {
|
|
553
|
+
const queryParams = {
|
|
554
|
+
"posts.title:contains@none": "spam",
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const criteria =
|
|
558
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
559
|
+
const filters = criteria.getFilters();
|
|
560
|
+
|
|
561
|
+
expect(filters).toHaveLength(1);
|
|
562
|
+
expect(filters[0].options?.quantifier).toBe("none");
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("should work with in operator and quantifier", () => {
|
|
566
|
+
const queryParams = {
|
|
567
|
+
"posts.title:in@some": "test1,test2,test3",
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const criteria =
|
|
571
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
572
|
+
const filters = criteria.getFilters();
|
|
573
|
+
|
|
574
|
+
expect(filters).toHaveLength(1);
|
|
575
|
+
expect(filters[0].operator).toBe("in");
|
|
576
|
+
expect(filters[0].value).toEqual(["test1", "test2", "test3"]);
|
|
577
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("should work with between operator and quantifier", () => {
|
|
581
|
+
const queryParams = {
|
|
582
|
+
"posts.likes:between@some": "10,100",
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
const criteria =
|
|
586
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
587
|
+
const filters = criteria.getFilters();
|
|
588
|
+
|
|
589
|
+
expect(filters).toHaveLength(1);
|
|
590
|
+
expect(filters[0].operator).toBe("between");
|
|
591
|
+
expect(filters[0].value).toEqual([10, 100]);
|
|
592
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("should maintain backward compatibility without quantifier", () => {
|
|
596
|
+
const queryParams = {
|
|
597
|
+
"posts.title:contains": "test",
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
const criteria =
|
|
601
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
602
|
+
const filters = criteria.getFilters();
|
|
603
|
+
|
|
604
|
+
expect(filters).toHaveLength(1);
|
|
605
|
+
expect(filters[0].field).toBe("posts.title");
|
|
606
|
+
expect(filters[0].operator).toBe("contains");
|
|
607
|
+
expect(filters[0].value).toBe("test");
|
|
608
|
+
expect(filters[0].options).toBeUndefined();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("should throw error for invalid quantifier", () => {
|
|
612
|
+
const queryParams = {
|
|
613
|
+
"posts.title:contains@invalid": "test",
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
expect(() => {
|
|
617
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
618
|
+
}).toThrow("Invalid quantifier");
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("should handle multiple filters with mixed quantifiers", () => {
|
|
622
|
+
const queryParams = {
|
|
623
|
+
"posts.title:contains@some": "test",
|
|
624
|
+
"posts.content:equals@every": "content",
|
|
625
|
+
"name:contains": "John",
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
const criteria =
|
|
629
|
+
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
630
|
+
const filters = criteria.getFilters();
|
|
631
|
+
|
|
632
|
+
expect(filters).toHaveLength(3);
|
|
633
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
634
|
+
expect(filters[1].options?.quantifier).toBe("every");
|
|
635
|
+
expect(filters[2].options).toBeUndefined();
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
describe("fromObject with quantifier", () => {
|
|
640
|
+
it("should create criteria from object with quantifier", () => {
|
|
641
|
+
const criteria = Criteria.fromObject<UserWithPostsDto>({
|
|
642
|
+
filters: [
|
|
643
|
+
{
|
|
644
|
+
field: "posts.title",
|
|
645
|
+
operator: "contains",
|
|
646
|
+
value: "test",
|
|
647
|
+
options: { quantifier: "some" },
|
|
648
|
+
},
|
|
649
|
+
],
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
const filters = criteria.getFilters();
|
|
653
|
+
expect(filters).toHaveLength(1);
|
|
654
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it("should preserve quantifier when cloning", () => {
|
|
658
|
+
const original = Criteria.create<UserWithPostsDto>().whereSome(
|
|
659
|
+
"posts.title",
|
|
660
|
+
"contains",
|
|
661
|
+
"test"
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
const cloned = original.clone();
|
|
665
|
+
const filters = cloned.getFilters();
|
|
666
|
+
|
|
667
|
+
expect(filters).toHaveLength(1);
|
|
668
|
+
expect(filters[0].options?.quantifier).toBe("some");
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
describe("toJSON with quantifier", () => {
|
|
673
|
+
it("should serialize quantifier to JSON", () => {
|
|
674
|
+
const criteria = Criteria.create<UserWithPostsDto>()
|
|
675
|
+
.whereSome("posts.title", "contains", "test")
|
|
676
|
+
.whereEvery("posts.content", "equals", "content");
|
|
677
|
+
|
|
678
|
+
const json = criteria.toJSON();
|
|
679
|
+
|
|
680
|
+
expect(json.filters).toHaveLength(2);
|
|
681
|
+
expect(json.filters[0].options?.quantifier).toBe("some");
|
|
682
|
+
expect(json.filters[1].options?.quantifier).toBe("every");
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
describe("Adapter", () => {
|
|
688
|
+
type UserInDatabase = {
|
|
689
|
+
id: string;
|
|
690
|
+
name: string;
|
|
691
|
+
user_posts: { title: string; content: string }[];
|
|
692
|
+
leads: {
|
|
693
|
+
contact: {
|
|
694
|
+
fullName: string;
|
|
695
|
+
};
|
|
696
|
+
};
|
|
697
|
+
};
|
|
698
|
+
const UserWithPostsAdapter: CriteriaAdapter<
|
|
699
|
+
UserWithPostsDto,
|
|
700
|
+
UserInDatabase
|
|
701
|
+
> = {
|
|
702
|
+
posts: "user_posts",
|
|
703
|
+
"leads.contact.name": "leads.contact.fullName",
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
let criteria: Criteria<UserWithPostsDto>;
|
|
707
|
+
beforeEach(() => {
|
|
708
|
+
criteria =
|
|
709
|
+
Criteria.create<UserWithPostsDto>().useAdapter(UserWithPostsAdapter);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it("should resolve field path", () => {
|
|
713
|
+
const filters = criteria
|
|
714
|
+
.where("leads.contact.name", "contains", "John")
|
|
715
|
+
.getFilters();
|
|
716
|
+
|
|
717
|
+
expect(filters[0].field).toBe("leads.contact.fullName");
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it("should resolve field path from query params", () => {
|
|
721
|
+
const queryParams = {
|
|
722
|
+
"posts:contains": "test",
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
const result = Criteria.fromQueryParams<UserWithPostsDto>(
|
|
726
|
+
queryParams,
|
|
727
|
+
UserWithPostsAdapter
|
|
728
|
+
);
|
|
729
|
+
const filters = result.getFilters();
|
|
730
|
+
|
|
731
|
+
expect(filters).toHaveLength(1);
|
|
732
|
+
expect(filters[0].field).toBe("user_posts");
|
|
733
|
+
expect(filters[0].operator).toBe("contains");
|
|
734
|
+
expect(filters[0].value).toBe("test");
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
it("should resolve field path from object", () => {
|
|
738
|
+
const criteria = Criteria.fromObject<UserWithPostsDto>(
|
|
739
|
+
{
|
|
740
|
+
filters: [
|
|
741
|
+
{
|
|
742
|
+
field: "posts",
|
|
743
|
+
operator: "in",
|
|
744
|
+
value: [{ title: "test", content: "test" }],
|
|
745
|
+
},
|
|
746
|
+
],
|
|
747
|
+
},
|
|
748
|
+
UserWithPostsAdapter
|
|
749
|
+
);
|
|
750
|
+
const filters = criteria.getFilters();
|
|
751
|
+
expect(filters).toHaveLength(1);
|
|
752
|
+
expect(filters[0].field).toBe("user_posts");
|
|
753
|
+
});
|
|
754
|
+
});
|
|
432
755
|
});
|