@blackcode_sa/metaestetics-api 1.11.3 → 1.12.1
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/admin/index.d.mts +378 -334
- package/dist/admin/index.d.ts +378 -334
- package/dist/backoffice/index.d.mts +1198 -430
- package/dist/backoffice/index.d.ts +1198 -430
- package/dist/backoffice/index.js +1128 -245
- package/dist/backoffice/index.mjs +1119 -209
- package/dist/index.d.mts +4478 -4031
- package/dist/index.d.ts +4478 -4031
- package/dist/index.js +1974 -757
- package/dist/index.mjs +1735 -490
- package/package.json +1 -1
- package/src/backoffice/expo-safe/index.ts +4 -0
- package/src/backoffice/services/README.md +40 -0
- package/src/backoffice/services/brand.service.ts +85 -6
- package/src/backoffice/services/category.service.ts +92 -10
- package/src/backoffice/services/constants.service.ts +308 -0
- package/src/backoffice/services/documentation-template.service.ts +56 -2
- package/src/backoffice/services/index.ts +1 -0
- package/src/backoffice/services/product.service.ts +126 -5
- package/src/backoffice/services/requirement.service.ts +13 -0
- package/src/backoffice/services/subcategory.service.ts +184 -13
- package/src/backoffice/services/technology.service.ts +344 -129
- package/src/backoffice/types/admin-constants.types.ts +69 -0
- package/src/backoffice/types/brand.types.ts +1 -0
- package/src/backoffice/types/index.ts +2 -0
- package/src/backoffice/types/procedure-product.types.ts +38 -0
- package/src/backoffice/types/product.types.ts +31 -4
- package/src/backoffice/types/static/contraindication.types.ts +1 -0
- package/src/backoffice/types/static/treatment-benefit.types.ts +1 -0
- package/src/backoffice/types/technology.types.ts +113 -4
- package/src/backoffice/validations/schemas.ts +35 -9
- package/src/services/appointment/appointment.service.ts +0 -5
- package/src/services/appointment/utils/appointment.utils.ts +124 -113
- package/src/services/base.service.ts +10 -3
- package/src/services/documentation-templates/documentation-template.service.ts +116 -0
- package/src/services/media/media.service.ts +2 -2
- package/src/services/practitioner/practitioner.service.ts +201 -83
- package/src/services/procedure/README.md +76 -1
- package/src/services/procedure/procedure.service.ts +538 -235
- package/src/types/appointment/index.ts +2 -3
- package/src/types/clinic/index.ts +1 -6
- package/src/types/patient/medical-info.types.ts +3 -3
- package/src/types/procedure/index.ts +39 -20
- package/src/validations/clinic.schema.ts +1 -6
- package/src/validations/patient/medical-info.schema.ts +7 -2
- package/src/validations/procedure-product.schema.ts +41 -0
- package/src/validations/procedure.schema.ts +59 -8
- package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
- package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
- package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
- package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
- package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
- package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
|
@@ -3,10 +3,9 @@ import { ClinicInfo, PractitionerProfileInfo, PatientProfileInfo } from '../prof
|
|
|
3
3
|
import { ProcedureSummaryInfo } from '../procedure';
|
|
4
4
|
import { Currency, type PricingMeasure } from '../../backoffice/types/static/pricing.types';
|
|
5
5
|
import { BlockingCondition } from '../../backoffice/types/static/blocking-condition.types';
|
|
6
|
-
import { Contraindication } from '../../backoffice/types/static/contraindication.types';
|
|
7
6
|
import { Requirement } from '../../backoffice/types/requirement.types';
|
|
8
7
|
import { FilledDocumentStatus } from '../documentation-templates';
|
|
9
|
-
import type { ProcedureFamily } from '../../backoffice';
|
|
8
|
+
import type { ContraindicationDynamic, ProcedureFamily } from '../../backoffice';
|
|
10
9
|
import type { MediaResource } from '../../services/media/media.service';
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -243,7 +242,7 @@ export interface Appointment {
|
|
|
243
242
|
|
|
244
243
|
/** Procedure-related conditions and requirements */
|
|
245
244
|
blockingConditions: BlockingCondition[];
|
|
246
|
-
contraindications:
|
|
245
|
+
contraindications: ContraindicationDynamic[];
|
|
247
246
|
preProcedureRequirements: Requirement[];
|
|
248
247
|
postProcedureRequirements: Requirement[];
|
|
249
248
|
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { Timestamp, FieldValue } from "firebase/firestore";
|
|
2
|
-
|
|
3
|
-
import type { TreatmentBenefit } from "../../backoffice/types/static/treatment-benefit.types";
|
|
4
|
-
import type {
|
|
5
|
-
Currency,
|
|
6
|
-
PricingMeasure,
|
|
7
|
-
} from "../../backoffice/types/static/pricing.types";
|
|
2
|
+
|
|
8
3
|
import type { ClinicInfo } from "../profile";
|
|
9
4
|
import { ClinicReviewInfo } from "../reviews";
|
|
10
5
|
import { ProcedureSummaryInfo } from "../procedure";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Timestamp } from "firebase/firestore";
|
|
2
2
|
import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
|
|
3
|
-
import { Contraindication } from "../../backoffice/types/static/contraindication.types";
|
|
4
3
|
import { AllergyType, AllergySubtype } from "./allergies";
|
|
4
|
+
import type { ContraindicationDynamic } from "../../backoffice";
|
|
5
5
|
|
|
6
6
|
export const PATIENT_MEDICAL_INFO_COLLECTION = "medical_info";
|
|
7
7
|
|
|
@@ -38,7 +38,7 @@ export interface PatientMedicalInfo {
|
|
|
38
38
|
}[];
|
|
39
39
|
|
|
40
40
|
contraindications: {
|
|
41
|
-
condition:
|
|
41
|
+
condition: ContraindicationDynamic;
|
|
42
42
|
lastOccurrence: Timestamp;
|
|
43
43
|
frequency: "rare" | "occasional" | "frequent";
|
|
44
44
|
isActive: boolean;
|
|
@@ -113,7 +113,7 @@ export interface UpdateBlockingConditionData
|
|
|
113
113
|
|
|
114
114
|
// Interfejsi za kontraindikacije
|
|
115
115
|
export interface AddContraindicationData {
|
|
116
|
-
condition:
|
|
116
|
+
condition: ContraindicationDynamic;
|
|
117
117
|
lastOccurrence: Timestamp;
|
|
118
118
|
frequency: "rare" | "occasional" | "frequent";
|
|
119
119
|
isActive: boolean;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BlockingCondition,
|
|
3
|
+
CertificationRequirement,
|
|
4
|
+
ProcedureFamily,
|
|
5
|
+
} from "../../backoffice/types";
|
|
6
|
+
import {
|
|
7
|
+
ContraindicationDynamic,
|
|
8
|
+
TreatmentBenefitDynamic,
|
|
9
|
+
} from "../../backoffice/types/admin-constants.types";
|
|
10
|
+
import { Requirement } from "../../backoffice/types/requirement.types";
|
|
2
11
|
import { Category } from "../../backoffice/types/category.types";
|
|
3
12
|
import { Subcategory } from "../../backoffice/types/subcategory.types";
|
|
4
|
-
import {
|
|
5
|
-
Technology,
|
|
6
|
-
type TechnologyDocumentationTemplate,
|
|
7
|
-
} from "../../backoffice/types/technology.types";
|
|
13
|
+
import { Technology } from "../../backoffice/types/technology.types";
|
|
8
14
|
import { Product } from "../../backoffice/types/product.types";
|
|
15
|
+
import { ClinicInfo, DoctorInfo } from "../../types/";
|
|
16
|
+
import { ProcedureReviewInfo } from "../reviews";
|
|
17
|
+
import { TechnologyDocumentationTemplate } from "../../backoffice/types/technology.types";
|
|
9
18
|
import {
|
|
10
19
|
PricingMeasure,
|
|
11
20
|
Currency,
|
|
12
21
|
} from "../../backoffice/types/static/pricing.types";
|
|
13
|
-
import { Requirement } from "../../backoffice/types/requirement.types";
|
|
14
|
-
import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
|
|
15
|
-
import { TreatmentBenefit } from "../../backoffice/types/static/treatment-benefit.types";
|
|
16
|
-
import { CertificationRequirement } from "../../backoffice/types/static/certification.types";
|
|
17
|
-
import { DocumentTemplate } from "../documentation-templates";
|
|
18
|
-
import { ClinicInfo } from "../profile";
|
|
19
|
-
import { DoctorInfo } from "../clinic";
|
|
20
|
-
import { PRACTITIONERS_COLLECTION } from "../practitioner";
|
|
21
|
-
import { ProcedureReviewInfo } from "../reviews";
|
|
22
|
-
import type { Contraindication } from "../../backoffice/types/static/contraindication.types";
|
|
23
22
|
import { MediaResource } from "../../services/media/media.service";
|
|
23
|
+
import type { ProcedureProduct } from "../../backoffice/types/procedure-product.types";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
|
|
@@ -45,22 +45,27 @@ export interface Procedure {
|
|
|
45
45
|
subcategory: Subcategory;
|
|
46
46
|
/** Technology used in this procedure */
|
|
47
47
|
technology: Technology;
|
|
48
|
-
/**
|
|
48
|
+
/** Default product used in this procedure */
|
|
49
49
|
product: Product;
|
|
50
|
-
/**
|
|
50
|
+
/** Default price of the procedure */
|
|
51
51
|
price: number;
|
|
52
52
|
/** Currency for the price */
|
|
53
53
|
currency: Currency;
|
|
54
|
-
/** How the price is measured (per ml, per zone, etc.) */
|
|
54
|
+
/** How the price is measured (per ml, per zone, etc.) - for default product*/
|
|
55
55
|
pricingMeasure: PricingMeasure;
|
|
56
56
|
/** Duration of the procedure in minutes */
|
|
57
|
+
productsMetadata: ProcedureProduct[];
|
|
57
58
|
duration: number;
|
|
58
59
|
/** Blocking conditions that prevent this procedure */
|
|
59
60
|
blockingConditions: BlockingCondition[];
|
|
60
61
|
/** Treatment benefits of this procedure */
|
|
61
|
-
treatmentBenefits:
|
|
62
|
+
treatmentBenefits: TreatmentBenefitDynamic[];
|
|
63
|
+
/** A list of just the string IDs of the treatment benefits, for efficient querying. */
|
|
64
|
+
treatmentBenefitIds: string[];
|
|
62
65
|
/** Contraindications of this procedure */
|
|
63
|
-
contraindications:
|
|
66
|
+
contraindications: ContraindicationDynamic[];
|
|
67
|
+
/** A list of just the string IDs of the contraindications, for efficient querying. */
|
|
68
|
+
contraindicationIds: string[];
|
|
64
69
|
/** Pre-procedure requirements */
|
|
65
70
|
preRequirements: Requirement[];
|
|
66
71
|
/** Post-procedure requirements */
|
|
@@ -101,6 +106,13 @@ export interface CreateProcedureData {
|
|
|
101
106
|
technologyId: string;
|
|
102
107
|
productId: string;
|
|
103
108
|
price: number;
|
|
109
|
+
productsMetadata: {
|
|
110
|
+
productId: string;
|
|
111
|
+
price: number;
|
|
112
|
+
currency: Currency;
|
|
113
|
+
pricingMeasure: PricingMeasure;
|
|
114
|
+
isDefault?: boolean;
|
|
115
|
+
}[];
|
|
104
116
|
currency: Currency;
|
|
105
117
|
pricingMeasure: PricingMeasure;
|
|
106
118
|
duration: number;
|
|
@@ -120,6 +132,13 @@ export interface UpdateProcedureData {
|
|
|
120
132
|
price?: number;
|
|
121
133
|
currency?: Currency;
|
|
122
134
|
pricingMeasure?: PricingMeasure;
|
|
135
|
+
productsMetadata?: {
|
|
136
|
+
productId: string;
|
|
137
|
+
price: number;
|
|
138
|
+
currency: Currency;
|
|
139
|
+
pricingMeasure: PricingMeasure;
|
|
140
|
+
isDefault?: boolean;
|
|
141
|
+
}[];
|
|
123
142
|
duration?: number;
|
|
124
143
|
isActive?: boolean;
|
|
125
144
|
practitionerId?: string;
|
|
@@ -7,12 +7,7 @@ import {
|
|
|
7
7
|
PracticeType,
|
|
8
8
|
Language,
|
|
9
9
|
} from "../types/clinic";
|
|
10
|
-
|
|
11
|
-
import { TreatmentBenefit } from "../backoffice/types/static/treatment-benefit.types";
|
|
12
|
-
import {
|
|
13
|
-
Currency,
|
|
14
|
-
PricingMeasure,
|
|
15
|
-
} from "../backoffice/types/static/pricing.types";
|
|
10
|
+
|
|
16
11
|
import { clinicReviewInfoSchema } from "./reviews.schema";
|
|
17
12
|
import {
|
|
18
13
|
procedureSummaryInfoSchema,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
CosmeticAllergySubtype,
|
|
8
8
|
} from "../../types/patient/allergies";
|
|
9
9
|
import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
|
|
10
|
-
import { Contraindication } from "../../backoffice/types/static/contraindication.types";
|
|
11
10
|
import { timestampSchema } from "../common.schema";
|
|
12
11
|
|
|
13
12
|
export const allergySubtypeSchema = z.union([
|
|
@@ -50,8 +49,14 @@ export const blockingConditionSchema = z.object({
|
|
|
50
49
|
isActive: z.boolean(),
|
|
51
50
|
});
|
|
52
51
|
|
|
52
|
+
export const contraindicationDynamicSchema = z.object({
|
|
53
|
+
id: z.string(),
|
|
54
|
+
name: z.string(),
|
|
55
|
+
description: z.string().optional(),
|
|
56
|
+
});
|
|
57
|
+
|
|
53
58
|
export const contraindicationSchema = z.object({
|
|
54
|
-
condition:
|
|
59
|
+
condition: contraindicationDynamicSchema,
|
|
55
60
|
lastOccurrence: timestampSchema,
|
|
56
61
|
frequency: z.enum(["rare", "occasional", "frequent"]),
|
|
57
62
|
notes: z.string().optional().nullable(),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
Currency,
|
|
4
|
+
PricingMeasure,
|
|
5
|
+
} from "../backoffice/types/static/pricing.types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schema for validating procedure product data.
|
|
9
|
+
* This is used when creating or updating a procedure to validate the products associated with it.
|
|
10
|
+
*/
|
|
11
|
+
export const procedureProductDataSchema = z.object({
|
|
12
|
+
/**
|
|
13
|
+
* The ID of the product. Must be a non-empty string.
|
|
14
|
+
* @validation
|
|
15
|
+
*/
|
|
16
|
+
productId: z.string().min(1, "Product ID is required"),
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The price of the product. Must be a non-negative number.
|
|
20
|
+
* @validation
|
|
21
|
+
*/
|
|
22
|
+
price: z.number().min(0, "Price must be a non-negative number"),
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The currency for the price. Must be one of the values from the Currency enum.
|
|
26
|
+
* @validation
|
|
27
|
+
*/
|
|
28
|
+
currency: z.nativeEnum(Currency),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The pricing measure for the product. Must be one of the values from the PricingMeasure enum.
|
|
32
|
+
* @validation
|
|
33
|
+
*/
|
|
34
|
+
pricingMeasure: z.nativeEnum(PricingMeasure),
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether this is the default product for the procedure.
|
|
38
|
+
* @validation
|
|
39
|
+
*/
|
|
40
|
+
isDefault: z.boolean().optional(),
|
|
41
|
+
});
|
|
@@ -1,9 +1,45 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { ProcedureFamily } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
|
|
3
|
+
import {
|
|
4
|
+
Currency,
|
|
5
|
+
PricingMeasure,
|
|
6
|
+
} from "../backoffice/types/static/pricing.types";
|
|
7
|
+
import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
|
|
8
|
+
import { procedureReviewInfoSchema } from "./reviews.schema";
|
|
9
|
+
import { mediaResourceSchema } from "./media.schema";
|
|
10
|
+
import { procedureProductDataSchema } from "./procedure-product.schema";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Schema for validating stored procedure product data (with full product objects).
|
|
14
|
+
* This is used when validating complete procedure documents from Firestore.
|
|
15
|
+
*/
|
|
16
|
+
export const storedProcedureProductSchema = z.object({
|
|
17
|
+
/**
|
|
18
|
+
* The full product object used in the procedure.
|
|
19
|
+
*/
|
|
20
|
+
product: z.any(), // We'll validate the full product object separately
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The price of the procedure when using this specific product.
|
|
24
|
+
*/
|
|
25
|
+
price: z.number().min(0, "Price must be a non-negative number"),
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The currency for the price of this product.
|
|
29
|
+
*/
|
|
30
|
+
currency: z.nativeEnum(Currency),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* How the price is measured (e.g., per ml, per zone).
|
|
34
|
+
*/
|
|
35
|
+
pricingMeasure: z.nativeEnum(PricingMeasure),
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether this is the default product for the procedure.
|
|
39
|
+
*/
|
|
40
|
+
isDefault: z.boolean().optional(),
|
|
41
|
+
});
|
|
42
|
+
|
|
7
43
|
/**
|
|
8
44
|
* Schema for creating a new procedure
|
|
9
45
|
*/
|
|
@@ -20,6 +56,7 @@ export const createProcedureSchema = z.object({
|
|
|
20
56
|
price: z.number().min(0),
|
|
21
57
|
currency: z.nativeEnum(Currency),
|
|
22
58
|
pricingMeasure: z.nativeEnum(PricingMeasure),
|
|
59
|
+
productsMetadata: z.array(procedureProductDataSchema).min(1),
|
|
23
60
|
duration: z.number().min(1).max(480), // Max 8 hours
|
|
24
61
|
practitionerId: z.string().min(1),
|
|
25
62
|
clinicBranchId: z.string().min(1),
|
|
@@ -36,6 +73,7 @@ export const updateProcedureSchema = z.object({
|
|
|
36
73
|
price: z.number().min(0).optional(),
|
|
37
74
|
currency: z.nativeEnum(Currency).optional(),
|
|
38
75
|
pricingMeasure: z.nativeEnum(PricingMeasure).optional(),
|
|
76
|
+
productsMetadata: z.array(procedureProductDataSchema).min(1).optional(),
|
|
39
77
|
duration: z.number().min(0).optional(),
|
|
40
78
|
isActive: z.boolean().optional(),
|
|
41
79
|
practitionerId: z.string().optional(),
|
|
@@ -48,18 +86,31 @@ export const updateProcedureSchema = z.object({
|
|
|
48
86
|
});
|
|
49
87
|
|
|
50
88
|
/**
|
|
51
|
-
* Schema for validating a complete procedure object
|
|
89
|
+
* Schema for validating a complete procedure object (as stored in Firestore)
|
|
52
90
|
*/
|
|
53
|
-
export const procedureSchema =
|
|
91
|
+
export const procedureSchema = z.object({
|
|
54
92
|
id: z.string().min(1),
|
|
93
|
+
name: z.string().min(1).max(200),
|
|
55
94
|
nameLower: z.string().min(1).max(200),
|
|
95
|
+
description: z.string().min(1).max(2000),
|
|
96
|
+
family: z.nativeEnum(ProcedureFamily),
|
|
56
97
|
category: z.any(), // We'll validate the full category object separately
|
|
57
98
|
subcategory: z.any(), // We'll validate the full subcategory object separately
|
|
58
99
|
technology: z.any(), // We'll validate the full technology object separately
|
|
59
100
|
product: z.any(), // We'll validate the full product object separately
|
|
101
|
+
productsMetadata: z.array(storedProcedureProductSchema).min(1), // Use stored format schema
|
|
102
|
+
price: z.number().min(0),
|
|
103
|
+
currency: z.nativeEnum(Currency),
|
|
104
|
+
pricingMeasure: z.nativeEnum(PricingMeasure),
|
|
105
|
+
duration: z.number().min(1).max(480),
|
|
106
|
+
practitionerId: z.string().min(1),
|
|
107
|
+
clinicBranchId: z.string().min(1),
|
|
108
|
+
photos: z.array(z.string()).optional(), // Stored as URL strings
|
|
60
109
|
blockingConditions: z.array(z.any()), // We'll validate blocking conditions separately
|
|
61
110
|
contraindications: z.array(z.any()), // We'll validate contraindications separately
|
|
111
|
+
contraindicationIds: z.array(z.string()), // Array of IDs for efficient querying
|
|
62
112
|
treatmentBenefits: z.array(z.any()), // We'll validate treatment benefits separately
|
|
113
|
+
treatmentBenefitIds: z.array(z.string()), // Array of IDs for efficient querying
|
|
63
114
|
preRequirements: z.array(z.any()), // We'll validate requirements separately
|
|
64
115
|
postRequirements: z.array(z.any()), // We'll validate requirements separately
|
|
65
116
|
certificationRequirement: z.any(), // We'll validate certification requirement separately
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { BrandService } from "../brand.service";
|
|
2
|
-
import { Brand } from "../../types/brand.types";
|
|
3
|
-
import {
|
|
4
|
-
collection,
|
|
5
|
-
doc,
|
|
6
|
-
getDoc,
|
|
7
|
-
getDocs,
|
|
8
|
-
addDoc,
|
|
9
|
-
updateDoc,
|
|
10
|
-
query,
|
|
11
|
-
where,
|
|
12
|
-
} from "firebase/firestore";
|
|
13
|
-
|
|
14
|
-
// Mock Firebase
|
|
15
|
-
jest.mock("firebase/firestore");
|
|
16
|
-
jest.mock("../../../config/firebase", () => ({
|
|
17
|
-
getFirebaseInstance: jest.fn().mockResolvedValue({
|
|
18
|
-
db: {},
|
|
19
|
-
auth: {},
|
|
20
|
-
}),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
const COLLECTION = "backoffice_brands";
|
|
24
|
-
|
|
25
|
-
describe("BrandService", () => {
|
|
26
|
-
let service: BrandService;
|
|
27
|
-
|
|
28
|
-
const mockBrand: Omit<Brand, "id" | "createdAt" | "updatedAt"> = {
|
|
29
|
-
name: "Test Brand",
|
|
30
|
-
manufacturer: "Test Manufacturer",
|
|
31
|
-
website: "https://test.com",
|
|
32
|
-
description: "Test Description",
|
|
33
|
-
isActive: true,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
jest.clearAllMocks();
|
|
38
|
-
service = new BrandService();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe("create", () => {
|
|
42
|
-
it("treba da kreira novi brend sa ispravnim podacima", async () => {
|
|
43
|
-
const mockDocRef = { id: "test-id" };
|
|
44
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
45
|
-
(addDoc as jest.Mock).mockResolvedValue(mockDocRef);
|
|
46
|
-
|
|
47
|
-
const result = await service.create(mockBrand);
|
|
48
|
-
|
|
49
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
50
|
-
expect(addDoc).toHaveBeenCalledWith(
|
|
51
|
-
"brands-collection",
|
|
52
|
-
expect.objectContaining({
|
|
53
|
-
...mockBrand,
|
|
54
|
-
createdAt: expect.any(Date),
|
|
55
|
-
updatedAt: expect.any(Date),
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
expect(result).toEqual({
|
|
59
|
-
id: "test-id",
|
|
60
|
-
...mockBrand,
|
|
61
|
-
createdAt: expect.any(Date),
|
|
62
|
-
updatedAt: expect.any(Date),
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("getAll", () => {
|
|
68
|
-
it("treba da vrati sve aktivne brendove", async () => {
|
|
69
|
-
const mockDocs = [
|
|
70
|
-
{
|
|
71
|
-
id: "test-id-1",
|
|
72
|
-
data: () => ({
|
|
73
|
-
...mockBrand,
|
|
74
|
-
createdAt: new Date(),
|
|
75
|
-
updatedAt: new Date(),
|
|
76
|
-
}),
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "test-id-2",
|
|
80
|
-
data: () => ({
|
|
81
|
-
...mockBrand,
|
|
82
|
-
name: "Test Brand 2",
|
|
83
|
-
createdAt: new Date(),
|
|
84
|
-
updatedAt: new Date(),
|
|
85
|
-
}),
|
|
86
|
-
},
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
90
|
-
(query as jest.Mock).mockReturnValue("filtered-query");
|
|
91
|
-
(where as jest.Mock).mockReturnValue("where-clause");
|
|
92
|
-
(getDocs as jest.Mock).mockResolvedValue({ docs: mockDocs });
|
|
93
|
-
|
|
94
|
-
const result = await service.getAll();
|
|
95
|
-
|
|
96
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
97
|
-
expect(query).toHaveBeenCalledWith("brands-collection", "where-clause");
|
|
98
|
-
expect(where).toHaveBeenCalledWith("isActive", "==", true);
|
|
99
|
-
expect(result).toHaveLength(2);
|
|
100
|
-
expect(result[0]).toEqual({
|
|
101
|
-
id: "test-id-1",
|
|
102
|
-
...mockBrand,
|
|
103
|
-
createdAt: expect.any(Date),
|
|
104
|
-
updatedAt: expect.any(Date),
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("update", () => {
|
|
110
|
-
it("treba da ažurira postojeći brend", async () => {
|
|
111
|
-
const updateData = { name: "Updated Brand" };
|
|
112
|
-
|
|
113
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
114
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
115
|
-
(getDoc as jest.Mock).mockResolvedValue({
|
|
116
|
-
exists: () => true,
|
|
117
|
-
id: "test-id",
|
|
118
|
-
data: () => ({
|
|
119
|
-
...mockBrand,
|
|
120
|
-
...updateData,
|
|
121
|
-
createdAt: new Date(),
|
|
122
|
-
updatedAt: new Date(),
|
|
123
|
-
}),
|
|
124
|
-
});
|
|
125
|
-
(updateDoc as jest.Mock).mockResolvedValue(undefined);
|
|
126
|
-
|
|
127
|
-
const result = await service.update("test-id", updateData);
|
|
128
|
-
|
|
129
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
130
|
-
expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
|
|
131
|
-
expect(updateDoc).toHaveBeenCalledWith(
|
|
132
|
-
"doc-ref",
|
|
133
|
-
expect.objectContaining({
|
|
134
|
-
...updateData,
|
|
135
|
-
updatedAt: expect.any(Date),
|
|
136
|
-
})
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe("delete", () => {
|
|
142
|
-
it("treba da izvrši soft delete brenda", async () => {
|
|
143
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
144
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
145
|
-
(updateDoc as jest.Mock).mockResolvedValue(undefined);
|
|
146
|
-
|
|
147
|
-
await service.delete("test-id");
|
|
148
|
-
|
|
149
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
150
|
-
expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
|
|
151
|
-
expect(updateDoc).toHaveBeenCalledWith("doc-ref", {
|
|
152
|
-
isActive: false,
|
|
153
|
-
updatedAt: expect.any(Date),
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe("getById", () => {
|
|
159
|
-
it("treba da vrati brend po ID-u", async () => {
|
|
160
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
161
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
162
|
-
(getDoc as jest.Mock).mockResolvedValue({
|
|
163
|
-
exists: () => true,
|
|
164
|
-
id: "test-id",
|
|
165
|
-
data: () => ({
|
|
166
|
-
...mockBrand,
|
|
167
|
-
createdAt: new Date(),
|
|
168
|
-
updatedAt: new Date(),
|
|
169
|
-
}),
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const result = await service.getById("test-id");
|
|
173
|
-
|
|
174
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
175
|
-
expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
|
|
176
|
-
expect(result).toEqual({
|
|
177
|
-
id: "test-id",
|
|
178
|
-
...mockBrand,
|
|
179
|
-
createdAt: expect.any(Date),
|
|
180
|
-
updatedAt: expect.any(Date),
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("treba da vrati null ako brend ne postoji", async () => {
|
|
185
|
-
(collection as jest.Mock).mockReturnValue("brands-collection");
|
|
186
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
187
|
-
(getDoc as jest.Mock).mockResolvedValue({
|
|
188
|
-
exists: () => false,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const result = await service.getById("non-existent-id");
|
|
192
|
-
|
|
193
|
-
expect(result).toBeNull();
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
});
|